diff options
Diffstat (limited to 'cli/tests/integration')
34 files changed, 0 insertions, 31771 deletions
diff --git a/cli/tests/integration/bench_tests.rs b/cli/tests/integration/bench_tests.rs deleted file mode 100644 index f92006eb9..000000000 --- a/cli/tests/integration/bench_tests.rs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::url::Url; -use test_util as util; -use util::assert_contains; -use util::assert_not_contains; -use util::env_vars_for_npm_tests; -use util::TestContext; - -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!(bench_formatting { - args: "bench bench/bench_formatting.ts", - exit_code: 0, - output: "bench/bench_formatting.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/multiple_group.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!(before_unload_prevent_default { - args: "bench --quiet bench/before_unload_prevent_default.ts", - output: "bench/before_unload_prevent_default.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_run { - args: "bench --no-run bench/no_run.ts", - output: "bench/no_run.out", - exit_code: 1, -}); - -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_explicit_start_end { - args: "bench --quiet -A bench/explicit_start_and_end.ts", - output: "bench/explicit_start_and_end.out", - exit_code: 1, -}); - -itest_flaky!(bench_explicit_start_end_low_precision { - args: "bench --quiet -A bench/explicit_start_and_end_low_precision.ts", - output: "bench/explicit_start_and_end_low_precision.out", -}); - -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", -}); - -itest!(json_output { - args: "bench --json bench/pass.ts", - exit_code: 0, - output: "bench/pass.json.out", -}); - -#[test] -fn recursive_permissions_pledge() { - let context = TestContext::default(); - let output = context - .new_command() - .args("bench bench/recursive_permissions_pledge.js") - .run(); - output.assert_exit_code(1); - assert_contains!( - output.combined_output(), - "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(); - let context = TestContext::default(); - context - .new_command() - .args(format!("bench bench/file_protocol.ts {file_url}")) - .run() - .assert_matches_file("bench/file_protocol.out"); -} - -itest!(package_json_basic { - args: "bench", - output: "package_json/basic/lib.bench.out", - envs: env_vars_for_npm_tests(), - http_server: true, - cwd: Some("package_json/basic"), - copy_temp_dir: Some("package_json/basic"), - exit_code: 0, -}); - -itest!(bench_lock { - args: "bench", - http_server: true, - cwd: Some("lockfile/basic"), - exit_code: 10, - output: "lockfile/basic/fail.out", -}); - -itest!(bench_no_lock { - args: "bench --no-lock", - http_server: true, - cwd: Some("lockfile/basic"), - output: "lockfile/basic/bench.nolock.out", -}); - -#[test] -fn conditionally_loads_type_graph() { - let context = TestContext::default(); - let output = context - .new_command() - .args("bench --reload -L debug run/type_directives_js_main.js") - .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch() - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); - let output = context - .new_command() - .args("bench --reload -L debug --no-check run/type_directives_js_main.js") - .run(); - assert_not_contains!(output.combined_output(), "type_reference.d.ts"); -} diff --git a/cli/tests/integration/bundle_tests.rs b/cli/tests/integration/bundle_tests.rs deleted file mode 100644 index 08e3fb06a..000000000 --- a/cli/tests/integration/bundle_tests.rs +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2018-2024 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!(ts_without_extension { - args: "bundle --ext ts file_extensions/ts_without_extension", - output: "bundle/file_extensions/ts_without_extension.out", -}); - -itest!(js_without_extension { - args: "bundle --ext js file_extensions/js_without_extension", - output: "bundle/file_extensions/js_without_extension.out", -}); - -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 deleted file mode 100644 index 2aa0f9d8b..000000000 --- a/cli/tests/integration/cache_tests.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util::env_vars_for_npm_tests; -use test_util::TestContext; -use test_util::TestContextBuilder; - -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 xdg_cache_home_dir() { - let context = TestContext::with_http_server(); - let deno_dir = context.temp_dir(); - let xdg_cache_home = deno_dir.path().join("cache"); - context - .new_command() - .env_remove("HOME") - .env_remove("DENO_DIR") - .env_clear() - .env("XDG_CACHE_HOME", &xdg_cache_home) - .args( - "cache --reload --no-check http://localhost:4548/subdir/redirects/a.ts", - ) - .run() - .skip_output_check() - .assert_exit_code(0); - assert!(xdg_cache_home.read_dir().count() > 0); -} - -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", -}); - -itest!(package_json_basic { - args: "cache main.ts", - output: "package_json/basic/main.cache.out", - envs: env_vars_for_npm_tests(), - http_server: true, - cwd: Some("package_json/basic"), - copy_temp_dir: Some("package_json/basic"), - exit_code: 0, -}); - -#[test] -fn cache_matching_package_json_dep_should_not_install_all() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "package.json", - r#"{ "dependencies": { "@types/node": "18.8.2", "@denotest/esm-basic": "*" } }"#, - ); - let output = context - .new_command() - .args("cache npm:@types/node@18.8.2") - .run(); - output.assert_matches_text(concat!( - "Download http://localhost:4545/npm/registry/@types/node\n", - "Download http://localhost:4545/npm/registry/@types/node/node-18.8.2.tgz\n", - "Initialize @types/node@18.8.2\n", - )); -} - -// Regression test for https://github.com/denoland/deno/issues/17299 -#[test] -fn cache_put_overwrite() { - let test_context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - - let part_one = r#" - const req = new Request('http://localhost/abc'); - const res1 = new Response('res1'); - const res2 = new Response('res2'); - - const cache = await caches.open('test'); - - await cache.put(req, res1); - await cache.put(req, res2); - - const res = await cache.match(req).then((res) => res?.text()); - console.log(res); - "#; - - let part_two = r#" - const req = new Request("http://localhost/abc"); - const res1 = new Response("res1"); - const res2 = new Response("res2"); - - const cache = await caches.open("test"); - - // Swap the order of put() calls. - await cache.put(req, res2); - await cache.put(req, res1); - - const res = await cache.match(req).then((res) => res?.text()); - console.log(res); - "#; - - temp_dir.write("cache_put.js", part_one); - - let run_command = - test_context.new_command().args_vec(["run", "cache_put.js"]); - - let output = run_command.run(); - output.assert_matches_text("res2\n"); - output.assert_exit_code(0); - - // The wait will surface the bug as we check last written time - // when we overwrite a response. - std::thread::sleep(std::time::Duration::from_secs(1)); - - temp_dir.write("cache_put.js", part_two); - let output = run_command.run(); - output.assert_matches_text("res1\n"); - output.assert_exit_code(0); -} - -#[test] -fn loads_type_graph() { - let output = TestContext::default() - .new_command() - .args("cache --reload -L debug run/type_directives_js_main.js") - .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch() - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); -} diff --git a/cli/tests/integration/cert_tests.rs b/cli/tests/integration/cert_tests.rs deleted file mode 100644 index 9e89a9f3e..000000000 --- a/cli/tests/integration/cert_tests.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_tls::rustls; -use deno_tls::rustls_pemfile; -use deno_tls::rustls_tokio_stream::TlsStream; -use std::io::BufReader; -use std::io::Cursor; -use std::io::Read; -use std::sync::Arc; -use test_util as util; -use url::Url; -use util::testdata_path; -use util::TestContext; - -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, -}); - -// TODO(bartlomieju): reenable, this test was flaky on macOS CI during 1.30.3 release -// 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() { - let module_url = - Url::parse("https://localhost:5545/cert/cafile_url_imports.ts").unwrap(); - let context = TestContext::with_http_server(); - let cafile = testdata_path().join("tls/RootCA.pem"); - - context - .new_command() - .args(format!("cache {module_url}")) - .env("DENO_CERT", cafile) - .run() - .assert_exit_code(0) - .skip_output_check(); -} - -#[flaky_test::flaky_test] -fn cafile_fetch() { - let module_url = - Url::parse("http://localhost:4545/cert/cafile_url_imports.ts").unwrap(); - let context = TestContext::with_http_server(); - let cafile = testdata_path().join("tls/RootCA.pem"); - context - .new_command() - .args(format!("cache --quiet --cert {} {}", cafile, module_url,)) - .run() - .assert_exit_code(0) - .assert_matches_text(""); -} - -#[test] -fn cafile_compile() { - let context = TestContext::with_http_server(); - let temp_dir = context.temp_dir().path(); - let output_exe = if cfg!(windows) { - temp_dir.join("cert.exe") - } else { - temp_dir.join("cert") - }; - let output = context.new_command() - .args(format!("compile --quiet --cert ./tls/RootCA.pem --allow-net --output {} ./cert/cafile_ts_fetch.ts", output_exe)) - .run(); - output.skip_output_check(); - - context - .new_command() - .name(output_exe) - .run() - .assert_matches_text("[WILDCARD]\nHello\n"); -} - -#[flaky_test::flaky_test] -fn cafile_install_remote_module() { - let context = TestContext::with_http_server(); - let temp_dir = context.temp_dir(); - let bin_dir = temp_dir.path().join("bin"); - bin_dir.create_dir_all(); - let cafile = util::testdata_path().join("tls/RootCA.pem"); - - let install_output = context - .new_command() - .args_vec([ - "install", - "--cert", - &cafile.to_string_lossy(), - "--root", - &temp_dir.path().to_string_lossy(), - "-n", - "echo_test", - "https://localhost:5545/echo.ts", - ]) - .split_output() - .run(); - println!("{}", install_output.stdout()); - eprintln!("{}", install_output.stderr()); - install_output.assert_exit_code(0); - - 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 = context - .new_command() - .name(echo_test_path) - .args("foo") - .env("PATH", util::target_dir()) - .run(); - output.assert_matches_text("[WILDCARD]foo"); -} - -#[flaky_test::flaky_test] -fn cafile_bundle_remote_exports() { - let context = TestContext::with_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 = context.temp_dir(); - let bundle = t.path().join("mod1.bundle.js"); - context - .new_command() - .args_vec([ - "bundle", - "--cert", - &cafile.to_string_lossy(), - mod1, - &bundle.to_string_lossy(), - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - assert!(bundle.is_file()); - - // Now we try to use that bundle from another module. - let test = t.path().join("test.js"); - test.write( - "import { printHello3 } from \"./mod1.bundle.js\"; -printHello3();", - ); - - context - .new_command() - .args_vec(["run", "--quiet", "--check", &test.to_string_lossy()]) - .run() - .assert_matches_text("[WILDCARD]Hello\n") - .assert_exit_code(0); -} - -#[tokio::test] -async fn listen_tls_alpn() { - 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_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, None); - - let handshake = tls_stream.handshake().await.unwrap(); - - assert_eq!(handshake.alpn, Some(b"foobar".to_vec())); - - let status = child.wait().unwrap(); - assert!(status.success()); -} - -#[tokio::test] -async fn listen_tls_alpn_fail() { - 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_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, None); - - tls_stream.handshake().await.unwrap_err(); - - let status = child.wait().unwrap(); - assert!(status.success()); -} diff --git a/cli/tests/integration/check_tests.rs b/cli/tests/integration/check_tests.rs deleted file mode 100644 index f836957ce..000000000 --- a/cli/tests/integration/check_tests.rs +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::env_vars_for_npm_tests; -use util::TestContext; -use util::TestContextBuilder; - -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/all/check_all.ts", - output: "check/all/check_all.out", - http_server: true, - exit_code: 1, -}); - -itest!(check_all_local { - args: "check --quiet check/all/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_jsximportsource_importmap_config { - args: "check --quiet --config check/jsximportsource_importmap_config/deno.json check/jsximportsource_importmap_config/main.tsx", - output_str: Some(""), -}); - -itest!(bundle_jsximportsource_importmap_config { - args: "bundle --quiet --config check/jsximportsource_importmap_config/deno.json check/jsximportsource_importmap_config/main.tsx", - output: "check/jsximportsource_importmap_config/main.bundle.js", -}); - -itest!(jsx_not_checked { - args: "check check/jsx_not_checked/main.jsx", - output: "check/jsx_not_checked/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -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_node_builtin_modules_ts { - args: "check --quiet check/node_builtin_modules/mod.ts", - output: "check/node_builtin_modules/mod.ts.out", - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); - -itest!(check_node_builtin_modules_js { - args: "check --quiet check/node_builtin_modules/mod.js", - output: "check/node_builtin_modules/mod.js.out", - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); - -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, -}); - -itest!(check_broadcast_channel { - args: "check --quiet check/broadcast_channel.ts", - exit_code: 0, -}); - -itest!(check_deno_not_found { - args: "check --quiet check/deno_not_found/main.ts", - output: "check/deno_not_found/main.out", - exit_code: 1, -}); - -itest!(check_with_exclude_option_by_dir { - args: - "check --quiet --config check/exclude_option/deno.exclude_dir.json check/exclude_option/ignored/index.ts", - output_str: Some(""), - exit_code: 0, -}); - -itest!(check_with_exclude_option_by_glob { - args: - "check --quiet --config check/exclude_option/deno.exclude_glob.json check/exclude_option/ignored/index.ts", - output_str: Some(""), - exit_code: 0, -}); - -itest!(check_without_exclude_option { - args: - "check --quiet --config check/exclude_option/deno.json check/exclude_option/ignored/index.ts", - output: "check/exclude_option/exclude_option.ts.error.out", - exit_code: 1, -}); - -itest!(check_imported_files_listed_in_exclude_option { - args: - "check --quiet --config check/exclude_option/deno.exclude_dir.json check/exclude_option/index.ts", - output: "check/exclude_option/exclude_option.ts.error.out", - exit_code: 1, -}); - -itest!(check_with_excluded_file_specified { - args: "check lib/types.d.ts", - cwd: Some("check/excluded_file_specified/"), - output: "check/excluded_file_specified/check.out", -}); - -#[test] -fn cache_switching_config_then_no_config() { - let context = TestContext::default(); - - assert!(does_type_checking(&context, true)); - assert!(does_type_checking(&context, false)); - - // should now not do type checking even when it changes - // configs because it previously did - assert!(!does_type_checking(&context, true)); - assert!(!does_type_checking(&context, false)); - - fn does_type_checking(context: &TestContext, with_config: bool) -> bool { - let mut args = vec![ - "check".to_string(), - "check/cache_config_on_off/main.ts".to_string(), - ]; - if with_config { - let mut slice = vec![ - "--config".to_string(), - "check/cache_config_on_off/deno.json".to_string(), - ]; - args.append(&mut slice); - } - - let output = context.new_command().args_vec(args).split_output().run(); - - output.assert_exit_code(0); - - let stderr = output.stderr(); - stderr.contains("Check") - } -} - -#[test] -fn reload_flag() { - // should do type checking whenever someone specifies --reload - let context = TestContext::default(); - - assert!(does_type_checking(&context, false)); - assert!(!does_type_checking(&context, false)); - assert!(does_type_checking(&context, true)); - assert!(does_type_checking(&context, true)); - assert!(!does_type_checking(&context, false)); - - fn does_type_checking(context: &TestContext, reload: bool) -> bool { - let mut args = vec![ - "check".to_string(), - "check/cache_config_on_off/main.ts".to_string(), - ]; - if reload { - let mut slice = vec!["--reload".to_string()]; - args.append(&mut slice); - } - let output = context.new_command().args_vec(args).split_output().run(); - output.assert_exit_code(0); - - let stderr = output.stderr(); - stderr.contains("Check") - } -} - -#[test] -fn typecheck_declarations_ns() { - let context = TestContext::default(); - let args = vec![ - "test".to_string(), - "--doc".to_string(), - util::root_path() - .join("cli/tsc/dts/lib.deno.ns.d.ts") - .to_string_lossy() - .into_owned(), - ]; - let output = context.new_command().args_vec(args).split_output().run(); - - println!("stdout: {}", output.stdout()); - println!("stderr: {}", output.stderr()); - output.assert_exit_code(0); -} - -#[test] -fn typecheck_declarations_unstable() { - let context = TestContext::default(); - let args = vec![ - "test".to_string(), - "--doc".to_string(), - "--unstable".to_string(), - util::root_path() - .join("cli/tsc/dts/lib.deno.unstable.d.ts") - .to_string_lossy() - .into_owned(), - ]; - let output = context.new_command().args_vec(args).split_output().run(); - - println!("stdout: {}", output.stdout()); - println!("stderr: {}", output.stderr()); - output.assert_exit_code(0); -} - -#[test] -fn ts_no_recheck_on_redirect() { - let test_context = TestContext::default(); - let check_command = test_context.new_command().args_vec([ - "run", - "--check", - "run/017_import_redirect.ts", - ]); - - // run once - let output = check_command.run(); - output.assert_matches_text("[WILDCARD]Check file://[WILDCARD]"); - - // run again - let output = check_command.run(); - output.assert_matches_text("Hello\n"); -} - -itest!(check_dts { - args: "check --quiet check/dts/check_dts.d.ts", - output: "check/dts/check_dts.out", - exit_code: 1, -}); - -itest!(check_types_dts { - args: "check main.ts", - cwd: Some("check/types_dts/"), - output: "check/types_dts/main.out", - exit_code: 0, -}); - -itest!(package_json_basic { - args: "check main.ts", - output: "package_json/basic/main.check.out", - envs: env_vars_for_npm_tests(), - http_server: true, - cwd: Some("package_json/basic"), - copy_temp_dir: Some("package_json/basic"), - exit_code: 0, -}); - -itest!(package_json_fail_check { - args: "check --quiet fail_check.ts", - output: "package_json/basic/fail_check.check.out", - envs: env_vars_for_npm_tests(), - http_server: true, - cwd: Some("package_json/basic"), - copy_temp_dir: Some("package_json/basic"), - exit_code: 1, -}); - -itest!(package_json_with_deno_json { - args: "check --quiet main.ts", - output: "package_json/deno_json/main.check.out", - cwd: Some("package_json/deno_json/"), - copy_temp_dir: Some("package_json/deno_json/"), - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -#[test] -fn check_error_in_dep_then_fix() { - let test_context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - let correct_code = - "export function greet(name: string) {\n return `Hello ${name}`;\n}\n"; - let incorrect_code = - "export function greet(name: number) {\n return `Hello ${name}`;\n}\n"; - - temp_dir.write( - "main.ts", - "import { greet } from './greet.ts';\n\nconsole.log(greet('world'));\n", - ); - temp_dir.write("greet.ts", incorrect_code); - - let check_command = test_context.new_command().args_vec(["check", "main.ts"]); - - let output = check_command.run(); - output.assert_matches_text("Check [WILDCARD]main.ts\nerror: TS234[WILDCARD]"); - output.assert_exit_code(1); - - temp_dir.write("greet.ts", correct_code); - let output = check_command.run(); - output.assert_matches_text("Check [WILDCARD]main.ts\n"); - - temp_dir.write("greet.ts", incorrect_code); - let output = check_command.run(); - output.assert_matches_text("Check [WILDCARD]main.ts\nerror: TS234[WILDCARD]"); - output.assert_exit_code(1); -} - -#[test] -fn json_module_check_then_error() { - let test_context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - let correct_code = "{ \"foo\": \"bar\" }"; - let incorrect_code = "{ \"foo2\": \"bar\" }"; - - temp_dir.write( - "main.ts", - "import test from './test.json' assert { type: 'json' }; console.log(test.foo);\n", - ); - temp_dir.write("test.json", correct_code); - - let check_command = test_context.new_command().args_vec(["check", "main.ts"]); - - check_command.run().assert_exit_code(0).skip_output_check(); - - temp_dir.write("test.json", incorrect_code); - check_command - .run() - .assert_matches_text("Check [WILDCARD]main.ts\nerror: TS2551[WILDCARD]") - .assert_exit_code(1); -} - -#[test] -fn npm_module_check_then_error() { - let test_context = TestContextBuilder::new() - .use_temp_cwd() - .add_npm_env_vars() - .use_http_server() - .build(); - let temp_dir = test_context.temp_dir(); - temp_dir.write("deno.json", "{}"); // so the lockfile gets loaded - - // get the lockfiles values first (this is necessary because the test - // server generates different tarballs based on the operating system) - test_context - .new_command() - .args_vec([ - "cache", - "npm:@denotest/breaking-change-between-versions@1.0.0", - "npm:@denotest/breaking-change-between-versions@2.0.0", - ]) - .run() - .skip_output_check(); - let lockfile = temp_dir.path().join("deno.lock"); - let mut lockfile_content = - lockfile.read_json::<deno_lockfile::LockfileContent>(); - - // make the specifier resolve to version 1 - lockfile_content.packages.specifiers.insert( - "npm:@denotest/breaking-change-between-versions".to_string(), - "npm:@denotest/breaking-change-between-versions@1.0.0".to_string(), - ); - lockfile.write_json(&lockfile_content); - temp_dir.write( - "main.ts", - "import { oldName } from 'npm:@denotest/breaking-change-between-versions'; console.log(oldName());\n", - ); - - let check_command = test_context.new_command().args_vec(["check", "main.ts"]); - check_command.run().assert_exit_code(0).skip_output_check(); - - // now update the lockfile to use version 2 instead, which should cause a - // type checking error because the oldName no longer exists - lockfile_content.packages.specifiers.insert( - "npm:@denotest/breaking-change-between-versions".to_string(), - "npm:@denotest/breaking-change-between-versions@2.0.0".to_string(), - ); - lockfile.write_json(&lockfile_content); - - check_command - .run() - .assert_matches_text("Check [WILDCARD]main.ts\nerror: TS2305[WILDCARD]has no exported member 'oldName'[WILDCARD]") - .assert_exit_code(1); -} diff --git a/cli/tests/integration/compile_tests.rs b/cli/tests/integration/compile_tests.rs deleted file mode 100644 index cf3bf023d..000000000 --- a/cli/tests/integration/compile_tests.rs +++ /dev/null @@ -1,1180 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::assert_contains; -use util::assert_not_contains; -use util::testdata_path; -use util::TestContext; -use util::TestContextBuilder; - -#[test] -fn compile_basic() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - 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 = context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "../../../cli/tests/testdata/welcome.ts", - ]) - .run(); - output.assert_exit_code(0); - output.skip_output_check(); - let output = context.new_command().name(&exe).run(); - output.assert_matches_text("Welcome to Deno!\n"); - } - - // now ensure this works when the deno_dir is readonly - let readonly_dir = dir.path().join("readonly"); - readonly_dir.make_dir_readonly(); - let readonly_sub_dir = readonly_dir.join("sub"); - - let output = context - .new_command() - // it should fail creating this, but still work - .env("DENO_DIR", readonly_sub_dir) - .name(exe) - .run(); - output.assert_matches_text("Welcome to Deno!\n"); -} - -#[test] -fn standalone_args() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("args.exe") - } else { - dir.path().join("args") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/args.ts", - "a", - "b", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .name(&exe) - .args("foo --bar --unstable") - .run() - .assert_matches_text("a\nb\nfoo\n--bar\n--unstable\n") - .assert_exit_code(0); -} - -#[test] -fn standalone_error() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("error.exe") - } else { - dir.path().join("error") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_error.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - let output = context.new_command().name(&exe).split_output().run(); - output.assert_exit_code(1); - output.assert_stdout_matches_text(""); - let stderr = output.stderr(); - // On Windows, we cannot assert the file path (because '\'). - // Instead we just check for relevant output. - assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!"); - assert_contains!(stderr, "throw new Error(\"boom!\");"); - assert_contains!(stderr, "\n at boom (file://"); - assert_contains!(stderr, "standalone_error.ts:2:9"); - assert_contains!(stderr, "at foo (file://"); - assert_contains!(stderr, "standalone_error.ts:5:3"); - assert_contains!(stderr, "standalone_error.ts:7:1"); -} - -#[test] -fn standalone_error_module_with_imports() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("error.exe") - } else { - dir.path().join("error") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_error_module_with_imports_1.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - let output = context - .new_command() - .name(&exe) - .env("NO_COLOR", "1") - .split_output() - .run(); - output.assert_stdout_matches_text("hello\n"); - let stderr = output.stderr(); - // On Windows, we cannot assert the file path (because '\'). - // Instead we just check for relevant output. - assert_contains!(stderr, "error: Uncaught (in promise) Error: boom!"); - assert_contains!(stderr, "throw new Error(\"boom!\");"); - assert_contains!(stderr, "\n at file://"); - assert_contains!(stderr, "standalone_error_module_with_imports_2.ts:2:7"); - output.assert_exit_code(1); -} - -#[test] -fn standalone_load_datauri() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("load_datauri.exe") - } else { - dir.path().join("load_datauri") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_import_datauri.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .name(&exe) - .run() - .assert_matches_text("Hello Deno!\n") - .assert_exit_code(0); -} - -// https://github.com/denoland/deno/issues/13704 -#[test] -fn standalone_follow_redirects() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("follow_redirects.exe") - } else { - dir.path().join("follow_redirects") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_follow_redirects.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .name(&exe) - .run() - .assert_matches_text("Hello\n") - .assert_exit_code(0); -} - -#[test] -fn compile_with_file_exists_error() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let output_path = if cfg!(windows) { - dir.path().join(r"args\") - } else { - dir.path().join("args/") - }; - let file_path = dir.path().join("args"); - file_path.write(""); - context - .new_command() - .args_vec([ - "compile", - "--output", - &output_path.to_string_lossy(), - "./compile/args.ts", - ]) - .run() - .assert_matches_text(&format!( - concat!( - "[WILDCARD]error: 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, - )) - .assert_exit_code(1); -} - -#[test] -fn compile_with_directory_exists_error() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("args.exe") - } else { - dir.path().join("args") - }; - std::fs::create_dir(&exe).unwrap(); - context.new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/args.ts" - ]).run() - .assert_matches_text(&format!( - concat!( - "[WILDCARD]error: 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.\n" - ), - exe - )) - .assert_exit_code(1); -} - -#[test] -fn compile_with_conflict_file_exists_error() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - 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(); - context.new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/args.ts" - ]).run() - .assert_matches_text(&format!( - concat!( - "[WILDCARD]error: 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.\n" - ), - exe - )) - .assert_exit_code(1); - exe.assert_matches_text("SHOULD NOT BE OVERWRITTEN"); -} - -#[test] -fn compile_and_overwrite_file() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("args.exe") - } else { - dir.path().join("args") - }; - - // do this twice - for _ in 0..2 { - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/args.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - assert!(&exe.exists()); - } -} - -#[test] -fn standalone_runtime_flags() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("flags.exe") - } else { - dir.path().join("flags") - }; - context - .new_command() - .args_vec([ - "compile", - "--allow-read", - "--seed", - "1", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_runtime_flags.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .env("NO_COLOR", "1") - .name(&exe) - .split_output() - .run() - .assert_stdout_matches_text("0.147205063401058\n") - .assert_stderr_matches_text( - "[WILDCARD]PermissionDenied: Requires write access to[WILDCARD]", - ) - .assert_exit_code(1); -} - -#[test] -fn standalone_ext_flag_ts() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("ext_flag_ts.exe") - } else { - dir.path().join("ext_flag_ts") - }; - context - .new_command() - .args_vec([ - "compile", - "--ext", - "ts", - "--output", - &exe.to_string_lossy(), - "./file_extensions/ts_without_extension", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .env("NO_COLOR", "1") - .name(&exe) - .run() - .assert_matches_text("executing typescript with no extension\n") - .assert_exit_code(0); -} - -#[test] -fn standalone_ext_flag_js() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("ext_flag_js.exe") - } else { - dir.path().join("ext_flag_js") - }; - context - .new_command() - .args_vec([ - "compile", - "--ext", - "js", - "--output", - &exe.to_string_lossy(), - "./file_extensions/js_without_extension", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .env("NO_COLOR", "1") - .name(&exe) - .run() - .assert_matches_text("executing javascript with no extension\n"); -} - -#[test] -fn standalone_import_map() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("import_map.exe") - } else { - dir.path().join("import_map") - }; - context - .new_command() - .args_vec([ - "compile", - "--allow-read", - "--import-map", - "compile/standalone_import_map.json", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_import_map.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .name(&exe) - .run() - .skip_output_check() - .assert_exit_code(0); -} - -#[test] -fn standalone_import_map_config_file() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("import_map.exe") - } else { - dir.path().join("import_map") - }; - context - .new_command() - .args_vec([ - "compile", - "--allow-read", - "--config", - "compile/standalone_import_map_config.json", - "--output", - &exe.to_string_lossy(), - "./compile/standalone_import_map.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - context - .new_command() - .name(&exe) - .run() - .skip_output_check() - .assert_exit_code(0); -} - -#[test] -// https://github.com/denoland/deno/issues/12670 -fn skip_rebundle() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("hello_world.exe") - } else { - dir.path().join("hello_world") - }; - let output = context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./run/001_hello.js", - ]) - .run(); - - //no "Bundle testdata_path/run/001_hello.js" in output - assert_not_contains!(output.combined_output(), "Bundle"); - - context - .new_command() - .name(&exe) - .run() - .assert_matches_text("Hello World\n") - .assert_exit_code(0); -} - -#[test] -fn check_local_by_default() { - let context = TestContext::with_http_server(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("welcome.exe") - } else { - dir.path().join("welcome") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/check_local_by_default.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); -} - -#[test] -fn check_local_by_default2() { - let context = TestContext::with_http_server(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("welcome.exe") - } else { - dir.path().join("welcome") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/check_local_by_default2.ts" - ]) - .run() - .assert_matches_text( - r#"[WILDCARD]error: TS2322 [ERROR]: Type '12' is not assignable to type '"b"'.[WILDCARD]"#, - ) - .assert_exit_code(1); -} - -#[test] -fn workers_basic() { - let context = TestContext::with_http_server(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("basic.exe") - } else { - dir.path().join("basic") - }; - context - .new_command() - .args_vec([ - "compile", - "--no-check", - "--output", - &exe.to_string_lossy(), - "./compile/workers/basic.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - context - .new_command() - .name(&exe) - .run() - .assert_matches_file("./compile/workers/basic.out") - .assert_exit_code(0); -} - -#[test] -fn workers_not_in_module_map() { - let context = TestContext::with_http_server(); - let temp_dir = context.temp_dir(); - let exe = if cfg!(windows) { - temp_dir.path().join("not_in_module_map.exe") - } else { - temp_dir.path().join("not_in_module_map") - }; - let output = context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/workers/not_in_module_map.ts", - ]) - .run(); - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context.new_command().name(exe).env("NO_COLOR", "").run(); - output.assert_exit_code(1); - output.assert_matches_text(concat!( - "error: Uncaught (in worker \"\") Module not found: [WILDCARD]", - "error: Uncaught (in promise) Error: Unhandled error in child worker.\n[WILDCARD]" - )); -} - -#[test] -fn workers_with_include_flag() { - let context = TestContext::with_http_server(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("workers_with_include_flag.exe") - } else { - dir.path().join("workers_with_include_flag") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "--include", - "./compile/workers/worker.ts", - "./compile/workers/not_in_module_map.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - context - .new_command() - .name(&exe) - .env("NO_COLOR", "") - .run() - .assert_matches_text("Hello from worker!\nReceived 42\nClosing\n"); -} - -#[test] -fn dynamic_import() { - let context = TestContext::with_http_server(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("dynamic_import.exe") - } else { - dir.path().join("dynamic_import") - }; - context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/dynamic_imports/main.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - context - .new_command() - .name(&exe) - .env("NO_COLOR", "") - .run() - .assert_matches_file("./compile/dynamic_imports/main.out") - .assert_exit_code(0); -} - -#[test] -fn dynamic_import_unanalyzable() { - let context = TestContext::with_http_server(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("dynamic_import_unanalyzable.exe") - } else { - dir.path().join("dynamic_import_unanalyzable") - }; - context - .new_command() - .args_vec([ - "compile", - "--allow-read", - "--include", - "./compile/dynamic_imports/import1.ts", - "--output", - &exe.to_string_lossy(), - "./compile/dynamic_imports/main_unanalyzable.ts", - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - context - .new_command() - .current_dir(util::root_path().join("cli")) - .name(&exe) - .env("NO_COLOR", "") - .run() - .assert_matches_file("./compile/dynamic_imports/main.out") - .assert_exit_code(0); -} - -#[test] -fn compile_npm_specifiers() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - - let temp_dir = context.temp_dir(); - temp_dir.write( - "main.ts", - concat!( - "import path from 'node:path';\n", - "import { getValue, setValue } from 'npm:@denotest/esm-basic';\n", - "import getValueDefault from 'npm:@denotest/esm-import-cjs-default';\n", - "setValue(2);\n", - "console.log(path.join('testing', 'this'));", - "console.log(getValue());", - "console.log(getValueDefault());", - ), - ); - - let binary_path = if cfg!(windows) { - temp_dir.path().join("binary.exe") - } else { - temp_dir.path().join("binary") - }; - - // try with and without --node-modules-dir - let compile_commands = &[ - "compile --output binary main.ts", - "compile --node-modules-dir --output binary main.ts", - ]; - - for compile_command in compile_commands { - let output = context.new_command().args(compile_command).run(); - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context.new_command().name(&binary_path).run(); - output.assert_matches_text( - r#"Node esm importing node cjs -=========================== -{ - default: [Function (anonymous)], - named: [Function (anonymous)], - MyClass: [class MyClass] -} -{ default: [Function (anonymous)], named: [Function (anonymous)] } -[Module: null prototype] { - MyClass: [class MyClass], - __esModule: true, - default: { - default: [Function (anonymous)], - named: [Function (anonymous)], - MyClass: [class MyClass] - }, - named: [Function (anonymous)] -} -[Module: null prototype] { - __esModule: true, - default: { default: [Function (anonymous)], named: [Function (anonymous)] }, - named: [Function (anonymous)] -} -=========================== -static method -testing[WILDCARD]this -2 -5 -"#, - ); - } - - // try with a package.json - temp_dir.remove_dir_all("node_modules"); - temp_dir.write( - "main.ts", - concat!( - "import { getValue, setValue } from '@denotest/esm-basic';\n", - "setValue(2);\n", - "console.log(getValue());", - ), - ); - temp_dir.write( - "package.json", - r#"{ "dependencies": { "@denotest/esm-basic": "1" } }"#, - ); - - context - .new_command() - .args("compile --output binary main.ts") - .run() - .assert_exit_code(0) - .skip_output_check(); - - context - .new_command() - .name(&binary_path) - .run() - .assert_matches_text("2\n"); - - // now try with byonm - temp_dir.remove_dir_all("node_modules"); - temp_dir.write("deno.json", r#"{"unstable":["byonm"]}"#); - context.run_npm("install"); - - context - .new_command() - .args("compile --output binary main.ts") - .run() - .assert_exit_code(0) - .assert_matches_text("Check file:///[WILDCARD]/main.ts\nCompile file:///[WILDCARD]/main.ts to binary[WILDCARD]\n"); - - context - .new_command() - .name(&binary_path) - .run() - .assert_matches_text("2\n"); -} - -#[test] -fn compile_npm_file_system() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "compile/npm_fs/main.ts", - compile_args: vec!["-A"], - run_args: vec![], - output_file: "compile/npm_fs/main.out", - node_modules_dir: true, - input_name: Some("binary"), - expected_name: "binary", - exit_code: 0, - }); -} - -#[test] -fn compile_npm_bin_esm() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "npm:@denotest/bin/cli-esm", - compile_args: vec![], - run_args: vec!["this", "is", "a", "test"], - output_file: "npm/deno_run_esm.out", - node_modules_dir: false, - input_name: None, - expected_name: "cli-esm", - exit_code: 0, - }); -} - -#[test] -fn compile_npm_bin_cjs() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "npm:@denotest/bin/cli-cjs", - compile_args: vec![], - run_args: vec!["this", "is", "a", "test"], - output_file: "npm/deno_run_cjs.out", - node_modules_dir: false, - input_name: None, - expected_name: "cli-cjs", - exit_code: 0, - }); -} - -#[test] -fn compile_npm_cowsay_main() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "npm:cowsay@1.5.0", - compile_args: vec!["--allow-read"], - run_args: vec!["Hello"], - output_file: "npm/deno_run_cowsay.out", - node_modules_dir: false, - input_name: None, - expected_name: "cowsay", - exit_code: 0, - }); -} - -#[test] -fn compile_npm_vfs_implicit_read_permissions() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "compile/vfs_implicit_read_permission/main.ts", - compile_args: vec![], - run_args: vec![], - output_file: "compile/vfs_implicit_read_permission/main.out", - node_modules_dir: false, - input_name: Some("binary"), - expected_name: "binary", - exit_code: 0, - }); -} - -#[test] -fn compile_npm_no_permissions() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "npm:cowsay@1.5.0", - compile_args: vec![], - run_args: vec!["Hello"], - output_file: "npm/deno_run_cowsay_no_permissions.out", - node_modules_dir: false, - input_name: None, - expected_name: "cowsay", - exit_code: 1, - }); -} - -#[test] -fn compile_npm_cowsay_explicit() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "npm:cowsay@1.5.0/cowsay", - compile_args: vec!["--allow-read"], - run_args: vec!["Hello"], - output_file: "npm/deno_run_cowsay.out", - node_modules_dir: false, - input_name: None, - expected_name: "cowsay", - exit_code: 0, - }); -} - -#[test] -fn compile_npm_cowthink() { - run_npm_bin_compile_test(RunNpmBinCompileOptions { - input_specifier: "npm:cowsay@1.5.0/cowthink", - compile_args: vec!["--allow-read"], - run_args: vec!["Hello"], - output_file: "npm/deno_run_cowthink.out", - node_modules_dir: false, - input_name: None, - expected_name: "cowthink", - exit_code: 0, - }); -} - -struct RunNpmBinCompileOptions<'a> { - input_specifier: &'a str, - node_modules_dir: bool, - output_file: &'a str, - input_name: Option<&'a str>, - expected_name: &'a str, - run_args: Vec<&'a str>, - compile_args: Vec<&'a str>, - exit_code: i32, -} - -fn run_npm_bin_compile_test(opts: RunNpmBinCompileOptions) { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - - let temp_dir = context.temp_dir(); - let main_specifier = if opts.input_specifier.starts_with("npm:") { - opts.input_specifier.to_string() - } else { - testdata_path().join(opts.input_specifier).to_string() - }; - - let mut args = vec!["compile".to_string()]; - - args.extend(opts.compile_args.iter().map(|s| s.to_string())); - - if opts.node_modules_dir { - args.push("--node-modules-dir".to_string()); - } - - if let Some(bin_name) = opts.input_name { - args.push("--output".to_string()); - args.push(bin_name.to_string()); - } - - args.push(main_specifier); - - // compile - let output = context.new_command().args_vec(args).run(); - output.assert_exit_code(0); - output.skip_output_check(); - - // delete the npm folder in the DENO_DIR to ensure it's not using it - context.deno_dir().remove_dir_all("./npm"); - - // run - let binary_path = if cfg!(windows) { - temp_dir.path().join(format!("{}.exe", opts.expected_name)) - } else { - temp_dir.path().join(opts.expected_name) - }; - let output = context - .new_command() - .name(binary_path) - .args_vec(opts.run_args) - .run(); - output.assert_matches_file(opts.output_file); - output.assert_exit_code(opts.exit_code); -} - -#[test] -fn compile_node_modules_symlink_outside() { - let context = TestContextBuilder::for_npm() - .use_copy_temp_dir("compile/node_modules_symlink_outside") - .cwd("compile/node_modules_symlink_outside") - .build(); - - let temp_dir = context.temp_dir(); - let project_dir = temp_dir - .path() - .join("compile") - .join("node_modules_symlink_outside"); - temp_dir.create_dir_all(project_dir.join("node_modules")); - temp_dir.create_dir_all(project_dir.join("some_folder")); - temp_dir.write(project_dir.join("test.txt"), "5"); - - // create a symlink in the node_modules directory that points to a folder in the cwd - temp_dir.symlink_dir( - project_dir.join("some_folder"), - project_dir.join("node_modules").join("some_folder"), - ); - // compile folder - let output = context - .new_command() - .args("compile --allow-read --node-modules-dir --output bin main.ts") - .run(); - output.assert_exit_code(0); - output.assert_matches_file( - "compile/node_modules_symlink_outside/main_compile_folder.out", - ); - assert!(project_dir.join("node_modules/some_folder").exists()); - - // Cleanup and remove the folder. The folder test is done separately from - // the file symlink test because different systems would traverse - // the directory items in different order. - temp_dir.remove_dir_all(project_dir.join("node_modules/some_folder")); - - // create a symlink in the node_modules directory that points to a file in the cwd - temp_dir.symlink_file( - project_dir.join("test.txt"), - project_dir.join("node_modules").join("test.txt"), - ); - assert!(project_dir.join("node_modules/test.txt").exists()); - - // compile - let output = context - .new_command() - .args("compile --allow-read --node-modules-dir --output bin main.ts") - .run(); - output.assert_exit_code(0); - output.assert_matches_file( - "compile/node_modules_symlink_outside/main_compile_file.out", - ); - - // run - let binary_path = - project_dir.join(if cfg!(windows) { "bin.exe" } else { "bin" }); - let output = context.new_command().name(binary_path).run(); - output.assert_matches_file("compile/node_modules_symlink_outside/main.out"); -} - -#[test] -fn compile_node_modules_symlink_non_existent() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir().path(); - temp_dir.join("main.ts").write( - r#"import { getValue, setValue } from "npm:@denotest/esm-basic"; -setValue(4); -console.log(getValue());"#, - ); - let node_modules_dir = temp_dir.join("node_modules"); - node_modules_dir.create_dir_all(); - // create a symlink that points to a non_existent file - node_modules_dir.symlink_dir("non_existent", "folder"); - // compile folder - let output = context - .new_command() - .args("compile --allow-read --node-modules-dir --output bin main.ts") - .run(); - output.assert_exit_code(0); - output.assert_matches_text( - r#"Download http://localhost:4545/npm/registry/@denotest/esm-basic -Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz -Initialize @denotest/esm-basic@1.0.0 -Check file:///[WILDCARD]/main.ts -Compile file:///[WILDCARD]/main.ts to [WILDCARD] -Warning Failed resolving symlink. Ignoring. - Path: [WILDCARD] - Message: [WILDCARD]) -"#, - ); - - // run - let binary_path = - temp_dir.join(if cfg!(windows) { "bin.exe" } else { "bin" }); - let output = context.new_command().name(binary_path).run(); - output.assert_matches_text("4\n"); -} - -#[test] -fn dynamic_imports_tmp_lit() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("app.exe") - } else { - dir.path().join("app") - }; - let output = context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "./compile/dynamic_imports_tmp_lit/main.js", - ]) - .run(); - output.assert_exit_code(0); - output.skip_output_check(); - let output = context.new_command().name(&exe).run(); - output.assert_matches_text("a\nb\n{ data: 5 }\n{ data: 1 }\n"); -} - -#[test] -fn granular_unstable_features() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("app.exe") - } else { - dir.path().join("app") - }; - let output = context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - "--unstable-kv", - "./compile/unstable_features.ts", - ]) - .run(); - output.assert_exit_code(0); - output.skip_output_check(); - let output = context.new_command().name(&exe).run(); - output.assert_exit_code(0); - output.assert_matches_text("Kv {}\n"); -} - -#[test] -fn dynamic_import_bad_data_uri() { - let context = TestContextBuilder::new().build(); - let dir = context.temp_dir(); - let exe = if cfg!(windows) { - dir.path().join("app.exe") - } else { - dir.path().join("app") - }; - let file = dir.path().join("bad_data_uri.ts"); - file.write("await import('data:application/')"); - let output = context - .new_command() - .args_vec([ - "compile", - "--output", - &exe.to_string_lossy(), - &file.to_string_lossy(), - ]) - .run(); - output.assert_exit_code(0); - output.skip_output_check(); - let output = context.new_command().name(&exe).run(); - output.assert_exit_code(1); - output.assert_matches_text( - "[WILDCARD]TypeError: Unable to decode data url.[WILDCARD]", - ); -} diff --git a/cli/tests/integration/coverage_tests.rs b/cli/tests/integration/coverage_tests.rs deleted file mode 100644 index 804f9b578..000000000 --- a/cli/tests/integration/coverage_tests.rs +++ /dev/null @@ -1,638 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::serde_json; -use std::fs; -use test_util as util; -use test_util::TempDir; -use util::assert_starts_with; -use util::env_vars_for_npm_tests; -use util::TestContext; -use util::TestContextBuilder; - -#[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"); -} - -// TODO(mmastrac): The exclusion to make this test pass doesn't seem to work on windows. -#[cfg_attr(windows, ignore)] -#[test] -fn no_tests() { - no_tests_included("foo", "mts"); - no_tests_included("foo", "ts"); - no_tests_included("foo", "js"); -} - -#[test] -fn error_if_invalid_cache() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir_path = context.temp_dir().path(); - let other_temp_dir = TempDir::new(); - let other_tempdir = other_temp_dir.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 = temp_dir_path.join("mod.ts"); - let mod_test_temp_path = temp_dir_path.join("mod.test.ts"); - - // Write the initial 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 output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - format!("--coverage={}", other_tempdir), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - // 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 = context - .new_command() - .args_vec(vec!["coverage".to_string(), format!("{}/", other_tempdir)]) - .run(); - - output.assert_exit_code(1); - let out = output.combined_output(); - - // Expect error - let error = util::strip_ansi_codes(out).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 context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "-A".to_string(), - "--quiet".to_string(), - format!("--coverage={}", tempdir), - format!("coverage/{test_name}_test.{extension}"), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--detailed".to_string(), - format!("{}/", tempdir), - ]) - .split_output() - .run(); - - // Verify there's no "Check" being printed - assert!(output.stderr().is_empty()); - - let actual = util::strip_ansi_codes(output.stdout()).to_string(); - - let expected = fs::read_to_string( - util::testdata_path().join(format!("coverage/{test_name}_expected.out")), - ) - .unwrap(); - - if !util::wildcard_match(&expected, &actual) { - println!("OUTPUT\n{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--quiet".to_string(), - "--lcov".to_string(), - format!("{}/", tempdir), - ]) - .run(); - - let actual = util::strip_ansi_codes(output.combined_output()).to_string(); - - let expected = fs::read_to_string( - util::testdata_path().join(format!("coverage/{test_name}_expected.lcov")), - ) - .unwrap(); - - if !util::wildcard_match(&expected, &actual) { - println!("OUTPUT\n{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); -} - -#[test] -fn multifile_coverage() { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - format!("--coverage={}", tempdir), - format!("coverage/multifile/"), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--detailed".to_string(), - format!("{}/", tempdir), - ]) - .split_output() - .run(); - - // Verify there's no "Check" being printed - assert!(output.stderr().is_empty()); - - let actual = util::strip_ansi_codes(output.stdout()).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{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - output.assert_exit_code(0); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--quiet".to_string(), - "--lcov".to_string(), - format!("{}/", tempdir), - ]) - .run(); - - let actual = util::strip_ansi_codes(output.combined_output()).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{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); -} - -fn no_snaps_included(test_name: &str, extension: &str) { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - "--allow-read".to_string(), - format!("--coverage={}", tempdir), - format!("coverage/no_snaps_included/{test_name}_test.{extension}"), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--include=no_snaps_included.ts".to_string(), - "--detailed".to_string(), - format!("{}/", tempdir), - ]) - .split_output() - .run(); - - // Verify there's no "Check" being printed - assert!(output.stderr().is_empty()); - - let actual = util::strip_ansi_codes(output.stdout()).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{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); -} - -fn no_tests_included(test_name: &str, extension: &str) { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - "--allow-read".to_string(), - format!("--coverage={}", tempdir), - format!("coverage/no_tests_included/{test_name}.test.{extension}"), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - format!("--exclude={}", util::std_path().canonicalize()), - "--detailed".to_string(), - format!("{}/", tempdir), - ]) - .split_output() - .run(); - - // Verify there's no "Check" being printed - assert!(output.stderr().is_empty()); - - let actual = util::strip_ansi_codes(output.stdout()).to_string(); - - let expected = fs::read_to_string( - util::testdata_path().join("coverage/no_tests_included/expected.out"), - ) - .unwrap(); - - if !util::wildcard_match(&expected, &actual) { - println!("OUTPUT\n{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); -} - -#[test] -fn no_npm_cache_coverage() { - let context = TestContext::with_http_server(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - "--allow-read".to_string(), - format!("--coverage={}", tempdir), - format!("coverage/no_npm_coverage/no_npm_coverage_test.ts"), - ]) - .envs(env_vars_for_npm_tests()) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--detailed".to_string(), - format!("{}/", tempdir), - ]) - .split_output() - .run(); - - // Verify there's no "Check" being printed - assert!(output.stderr().is_empty()); - - let actual = util::strip_ansi_codes(output.stdout()).to_string(); - - let expected = fs::read_to_string( - util::testdata_path().join("coverage/no_npm_coverage/expected.out"), - ) - .unwrap(); - - if !util::wildcard_match(&expected, &actual) { - println!("OUTPUT\n{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); -} - -#[test] -fn no_transpiled_lines() { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - format!("--coverage={}", tempdir), - "coverage/no_transpiled_lines/".to_string(), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--include=no_transpiled_lines/index.ts".to_string(), - "--detailed".to_string(), - format!("{}/", tempdir), - ]) - .run(); - - let actual = util::strip_ansi_codes(output.combined_output()).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{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--lcov".to_string(), - "--include=no_transpiled_lines/index.ts".to_string(), - format!("{}/", tempdir), - ]) - .run(); - - let actual = util::strip_ansi_codes(output.combined_output()).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{actual}\nOUTPUT"); - println!("EXPECTED\n{expected}\nEXPECTED"); - panic!("pattern match failed"); - } - - output.assert_exit_code(0); -} - -#[test] -fn no_internal_code() { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - format!("--coverage={}", tempdir), - "coverage/no_internal_code_test.ts".to_string(), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - // Check that coverage files contain no internal urls - let paths = fs::read_dir(tempdir).unwrap(); - for path in paths { - let unwrapped = path.unwrap().path(); - let data = fs::read_to_string(&unwrapped.clone()).unwrap(); - - let value: serde_json::Value = serde_json::from_str(&data).unwrap(); - let url = value["url"].as_str().unwrap(); - assert_starts_with!(url, "file:"); - } -} - -#[test] -fn no_internal_node_code() { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - "--no-check".to_string(), - format!("--coverage={}", tempdir), - "coverage/no_internal_node_code_test.ts".to_string(), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - // Check that coverage files contain no internal urls - let paths = fs::read_dir(tempdir).unwrap(); - for path in paths { - let unwrapped = path.unwrap().path(); - let data = fs::read_to_string(&unwrapped.clone()).unwrap(); - - let value: serde_json::Value = serde_json::from_str(&data).unwrap(); - let url = value["url"].as_str().unwrap(); - assert_starts_with!(url, "file:"); - } -} - -#[test] -fn test_html_reporter() { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - format!("--coverage={}", tempdir), - "coverage/multisource".to_string(), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec![ - "coverage".to_string(), - "--html".to_string(), - format!("{}/", tempdir), - ]) - .run(); - - output.assert_exit_code(0); - output.assert_matches_text("HTML coverage report has been generated at [WILDCARD]/cov/html/index.html\n"); - - let index_html = - fs::read_to_string(tempdir.join("html").join("index.html")).unwrap(); - assert!(index_html.contains("<h1>Coverage report for all files</h1>")); - assert!(index_html.contains("baz/")); - assert!(index_html.contains("href='baz/index.html'")); - assert!(index_html.contains("foo.ts")); - assert!(index_html.contains("href='foo.ts.html'")); - assert!(index_html.contains("bar.ts")); - assert!(index_html.contains("href='bar.ts.html'")); - - let foo_ts_html = - fs::read_to_string(tempdir.join("html").join("foo.ts.html")).unwrap(); - assert!(foo_ts_html.contains("<h1>Coverage report for foo.ts</h1>")); - - let bar_ts_html = - fs::read_to_string(tempdir.join("html").join("bar.ts.html")).unwrap(); - assert!(bar_ts_html.contains("<h1>Coverage report for bar.ts</h1>")); - // Check <T> in source code is escaped to <T> - assert!(bar_ts_html.contains("<T>")); - - let baz_index_html = - fs::read_to_string(tempdir.join("html").join("baz").join("index.html")) - .unwrap(); - assert!(baz_index_html.contains("<h1>Coverage report for baz/</h1>")); - assert!(baz_index_html.contains("qux.ts")); - assert!(baz_index_html.contains("href='qux.ts.html'")); - assert!(baz_index_html.contains("quux.ts")); - assert!(baz_index_html.contains("href='quux.ts.html'")); - - let baz_qux_ts_html = - fs::read_to_string(tempdir.join("html").join("baz").join("qux.ts.html")) - .unwrap(); - assert!(baz_qux_ts_html.contains("<h1>Coverage report for baz/qux.ts</h1>")); - - let baz_quux_ts_html = - fs::read_to_string(tempdir.join("html").join("baz").join("quux.ts.html")) - .unwrap(); - assert!(baz_quux_ts_html.contains("<h1>Coverage report for baz/quux.ts</h1>")); -} - -#[test] -fn test_summary_reporter() { - let context = TestContext::default(); - let tempdir = context.temp_dir(); - let tempdir = tempdir.path().join("cov"); - - let output = context - .new_command() - .args_vec(vec![ - "test".to_string(), - "--quiet".to_string(), - format!("--coverage={}", tempdir), - "coverage/multisource".to_string(), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .args_vec(vec!["coverage".to_string(), format!("{}/", tempdir)]) - .run(); - - output.assert_exit_code(0); - output.assert_matches_text( - "---------------------------------- -File | Branch % | Line % | ----------------------------------- - bar.ts | 0.0 | 57.1 | - baz/quux.ts | 0.0 | 28.6 | - baz/qux.ts | 100.0 | 100.0 | - foo.ts | 50.0 | 76.9 | ----------------------------------- - All files | 40.0 | 61.0 | ----------------------------------- -", - ); -} - -itest!(no_files_found { - args: "coverage doesnt_exist.js", - exit_code: 1, - output: "coverage/doesnt_exist.out", -}); diff --git a/cli/tests/integration/doc_tests.rs b/cli/tests/integration/doc_tests.rs deleted file mode 100644 index 62fd2a5b4..000000000 --- a/cli/tests/integration/doc_tests.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::assert_contains; -use util::TestContext; - -itest!(deno_doc_builtin { - args: "doc", - output: "doc/deno_doc_builtin.out", -}); - -#[test] -fn deno_doc() { - let context = TestContext::default(); - // try this twice to ensure it works with the cache - for _ in 0..2 { - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("doc doc/deno_doc.ts doc/deno_doc2.ts") - .split_output() - .run(); - - output.assert_exit_code(0); - assert_contains!(output.stdout(), "function foo"); - assert_contains!(output.stdout(), "function bar"); - } -} - -itest!(deno_doc_import_map { - args: "doc --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!(deno_doc_referenced_private_types { - args: "doc doc/referenced_private_types.ts", - output: "doc/referenced_private_types.out", -}); - -itest!(deno_doc_lint_referenced_private_types_error { - args: "doc --lint doc/referenced_private_types.ts", - exit_code: 1, - output: "doc/referenced_private_types_lint.out", -}); - -itest!(deno_doc_lint_referenced_private_types_fixed { - args: "doc --lint doc/referenced_private_types_fixed.ts", - output: "doc/referenced_private_types_fixed.out", -}); - -itest!(deno_doc_html_lint_referenced_private_types_fixed { - args: "doc --lint --html --name=Library doc/referenced_private_types.ts", - exit_code: 1, - output: "doc/referenced_private_types_lint.out", -}); - -itest!(deno_doc_lint_success { - args: "doc --lint doc/lint_success.ts", - output: "doc/lint_success.out", -}); - -itest!(deno_doc_lint_json_success { - args: "doc --lint --json doc/lint_success.ts", - output: "doc/lint_success_json.out", -}); - -itest!(deno_doc_lint_html_success { - args: "doc --lint --html --name=Library lint_success.ts", - copy_temp_dir: Some("doc"), - cwd: Some("doc"), - output: "doc/lint_success_html.out", -}); - -itest!(_060_deno_doc_displays_all_overloads_in_details_view { - args: - "doc --filter NS.test doc/060_deno_doc_displays_all_overloads_in_details_view.ts", - 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, -}); - -itest!(doc_lock { - args: "doc main.ts", - http_server: true, - cwd: Some("lockfile/basic"), - exit_code: 10, - output: "lockfile/basic/fail.out", -}); - -itest!(doc_no_lock { - args: "doc --no-lock main.ts", - http_server: true, - cwd: Some("lockfile/basic"), - output: "lockfile/basic/doc.nolock.out", -}); - -#[test] -fn deno_doc_html() { - let context = TestContext::default(); - let temp_dir = context.temp_dir(); - let output = context - .new_command() - .env("NO_COLOR", "1") - .args_vec(vec![ - "doc", - "--html", - "--name=MyLib", - &format!("--output={}", temp_dir.path().to_string_lossy()), - "doc/referenced_private_types_fixed.ts", - ]) - .split_output() - .run(); - - output.assert_exit_code(0); - assert_contains!(output.stderr(), "Written 10 files to"); - assert!(temp_dir.path().join("all_symbols.html").exists()); - assert!(temp_dir.path().join("index.html").exists()); - assert!(temp_dir.path().join("fuse.js").exists()); - assert!(temp_dir.path().join("page.css").exists()); - assert!(temp_dir.path().join("search.js").exists()); - assert!(temp_dir.path().join("search_index.js").exists()); - assert!(temp_dir.path().join("styles.css").exists()); - assert!(temp_dir.path().join("~/MyInterface.html").exists()); - assert!(temp_dir.path().join("~/MyClass.html").exists()); - assert!(temp_dir.path().join("~/index.html").exists()); -} diff --git a/cli/tests/integration/eval_tests.rs b/cli/tests/integration/eval_tests.rs deleted file mode 100644 index 1ae65e49e..000000000 --- a/cli/tests/integration/eval_tests.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2018-2024 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_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_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, -}); - -itest!(env_file { - args: "eval --env=env console.log(Deno.env.get(\"ANOTHER_FOO\"))", - output_str: Some("ANOTHER_BAR\n"), -}); - -itest!(env_file_missing { - args: "eval --env=missing console.log(Deno.env.get(\"ANOTHER_FOO\"))", - output: "eval/env_file_missing.out", -}); diff --git a/cli/tests/integration/flags_tests.rs b/cli/tests/integration/flags_tests.rs deleted file mode 100644 index a22cb0548..000000000 --- a/cli/tests/integration/flags_tests.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::assert_contains; - -#[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 help_output() { - let output = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("--help") - .run(); - - let stdout = output.combined_output(); - let subcommand_descriptions = vec![ - "Run a JavaScript or TypeScript program", - "Run benchmarks", - "Bundle module and dependencies into single file", - "Cache the dependencies", - "Type-check the dependencies", - "Compile the script into a self contained executable", - "Generate shell completions", - "Print coverage reports", - "Show documentation for a module", - "Eval script", - "Format source files", - "Initialize a new project", - "Show info about cache or info related to source file", - "Install script as an executable", - "Uninstall a script previously installed with deno install", - "Start the language server", - "Lint source files", - "Read Eval Print Loop", - "Run a task defined in the configuration file", - "Run tests", - "Print runtime TypeScript declarations", - #[cfg(feature = "upgrade")] - "Upgrade deno executable to given version", - "Vendor remote modules into a local directory", - "Print this message or the help of the given subcommand(s)", - ]; - - for description in subcommand_descriptions { - assert_contains!(stdout, description); - } -} - -#[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 deleted file mode 100644 index 94eca295e..000000000 --- a/cli/tests/integration/fmt_tests.rs +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::assert_contains; -use util::PathRef; -use util::TestContext; -use util::TestContextBuilder; - -#[test] -fn fmt_test() { - let context = TestContext::default(); - let t = context.deno_dir(); - 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"); - badly_formatted_original_js.copy(&badly_formatted_js); - - 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"); - badly_formatted_original_md.copy(&badly_formatted_md); - - 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"); - badly_formatted_original_json.copy(&badly_formatted_json); - - let fixed_ipynb = testdata_fmt_dir.join("badly_formatted_fixed.ipynb"); - let badly_formatted_original_ipynb = - testdata_fmt_dir.join("badly_formatted.ipynb"); - let badly_formatted_ipynb = t.path().join("badly_formatted.ipynb"); - badly_formatted_original_ipynb.copy(&badly_formatted_ipynb); - - // First, check formatting by ignoring the badly formatted file. - let output = context - .new_command() - .current_dir(&testdata_fmt_dir) - .args_vec(vec![ - "fmt".to_string(), - format!( - "--ignore={badly_formatted_js},{badly_formatted_md},{badly_formatted_json},{badly_formatted_ipynb}", - ), - format!( - "--check {badly_formatted_js} {badly_formatted_md} {badly_formatted_json} {badly_formatted_ipynb}", - ), - ]) - .run(); - - // No target files found - output.assert_exit_code(1); - output.skip_output_check(); - - // Check without ignore. - let output = context - .new_command() - .current_dir(&testdata_fmt_dir) - .args_vec(vec![ - "fmt".to_string(), - "--check".to_string(), - badly_formatted_js.to_string(), - badly_formatted_md.to_string(), - badly_formatted_json.to_string(), - badly_formatted_ipynb.to_string(), - ]) - .run(); - - output.assert_exit_code(1); - output.skip_output_check(); - - // Format the source file. - let output = context - .new_command() - .current_dir(&testdata_fmt_dir) - .args_vec(vec![ - "fmt".to_string(), - badly_formatted_js.to_string(), - badly_formatted_md.to_string(), - badly_formatted_json.to_string(), - badly_formatted_ipynb.to_string(), - ]) - .run(); - - output.assert_exit_code(0); - output.skip_output_check(); - - let expected_js = fixed_js.read_to_string(); - let expected_md = fixed_md.read_to_string(); - let expected_json = fixed_json.read_to_string(); - let expected_ipynb = fixed_ipynb.read_to_string(); - let actual_js = badly_formatted_js.read_to_string(); - let actual_md = badly_formatted_md.read_to_string(); - let actual_json = badly_formatted_json.read_to_string(); - let actual_ipynb = badly_formatted_ipynb.read_to_string(); - assert_eq!(expected_js, actual_js); - assert_eq!(expected_md, actual_md); - assert_eq!(expected_json, actual_json); - assert_eq!(expected_ipynb, actual_ipynb); -} - -#[test] -fn fmt_stdin_syntax_error() { - let output = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("fmt") - .arg("-") - .stdin_text("import { example }") - .split_output() - .run(); - assert!(output.stdout().is_empty()); - assert!(!output.stderr().is_empty()); - output.assert_exit_code(1); -} - -#[test] -fn fmt_ignore_unexplicit_files() { - let context = TestContext::default(); - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("fmt --check --ignore=./") - .run(); - - output.assert_exit_code(1); - assert_eq!(output.combined_output(), "error: No target files found.\n"); -} - -#[test] -fn fmt_auto_ignore_git_and_node_modules() { - fn create_bad_json(t: PathRef) { - let bad_json_path = t.join("bad.json"); - bad_json_path.write("bad json\n"); - } - - let context = TestContext::default(); - let temp_dir = context.temp_dir(); - 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"); - nest_git.create_dir_all(); - git_dir.create_dir_all(); - nest_node_modules.create_dir_all(); - node_modules_dir.create_dir_all(); - create_bad_json(nest_git); - create_bad_json(git_dir); - create_bad_json(nest_node_modules); - create_bad_json(node_modules_dir); - - let output = context - .new_command() - .current_dir(t) - .env("NO_COLOR", "1") - .args("fmt") - .run(); - - output.assert_exit_code(1); - assert_eq!(output.combined_output(), "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_check_invalid_data { - args: "fmt --check fmt/invalid_data.json", - output: "fmt/invalid_data.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_ipynb { - args: "fmt --ext=ipynb -", - input: Some(include_str!("../testdata/fmt/badly_formatted.ipynb")), - output_str: Some(include_str!("../testdata/fmt/badly_formatted_fixed.ipynb")), -}); - -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_deprecated_config { - args: - "fmt --config fmt/with_config/deno.deprecated.jsonc fmt/with_config/subdir", - output: "fmt/fmt_with_deprecated_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, -}); - -#[test] -fn fmt_with_glob_config() { - let context = TestContextBuilder::new().cwd("fmt").build(); - - let cmd_output = context - .new_command() - .args("fmt --check --config deno.glob.json") - .run(); - - cmd_output.assert_exit_code(1); - - let output = cmd_output.combined_output(); - if cfg!(windows) { - assert_contains!(output, r"glob\nested\fizz\fizz.ts"); - assert_contains!(output, r"glob\pages\[id].ts"); - assert_contains!(output, r"glob\nested\fizz\bar.ts"); - assert_contains!(output, r"glob\nested\foo\foo.ts"); - assert_contains!(output, r"glob\data\test1.js"); - assert_contains!(output, r"glob\nested\foo\bar.ts"); - assert_contains!(output, r"glob\nested\foo\fizz.ts"); - assert_contains!(output, r"glob\nested\fizz\foo.ts"); - assert_contains!(output, r"glob\data\test1.ts"); - } else { - assert_contains!(output, "glob/nested/fizz/fizz.ts"); - assert_contains!(output, "glob/pages/[id].ts"); - assert_contains!(output, "glob/nested/fizz/bar.ts"); - assert_contains!(output, "glob/nested/foo/foo.ts"); - assert_contains!(output, "glob/data/test1.js"); - assert_contains!(output, "glob/nested/foo/bar.ts"); - assert_contains!(output, "glob/nested/foo/fizz.ts"); - assert_contains!(output, "glob/nested/fizz/foo.ts"); - assert_contains!(output, "glob/data/test1.ts"); - } - - assert_contains!(output, "Found 9 not formatted files in 9 files"); -} - -#[test] -fn fmt_with_glob_config_and_flags() { - let context = TestContextBuilder::new().cwd("fmt").build(); - - let cmd_output = context - .new_command() - .args("fmt --check --config deno.glob.json --ignore=glob/nested/**/bar.ts") - .run(); - - cmd_output.assert_exit_code(1); - - let output = cmd_output.combined_output(); - if cfg!(windows) { - assert_contains!(output, r"glob\nested\fizz\fizz.ts"); - assert_contains!(output, r"glob\pages\[id].ts"); - assert_contains!(output, r"glob\nested\fizz\bazz.ts"); - assert_contains!(output, r"glob\nested\foo\foo.ts"); - assert_contains!(output, r"glob\data\test1.js"); - assert_contains!(output, r"glob\nested\foo\bazz.ts"); - assert_contains!(output, r"glob\nested\foo\fizz.ts"); - assert_contains!(output, r"glob\nested\fizz\foo.ts"); - assert_contains!(output, r"glob\data\test1.ts"); - } else { - assert_contains!(output, "glob/nested/fizz/fizz.ts"); - assert_contains!(output, "glob/pages/[id].ts"); - assert_contains!(output, "glob/nested/fizz/bazz.ts"); - assert_contains!(output, "glob/nested/foo/foo.ts"); - assert_contains!(output, "glob/data/test1.js"); - assert_contains!(output, "glob/nested/foo/bazz.ts"); - assert_contains!(output, "glob/nested/foo/fizz.ts"); - assert_contains!(output, "glob/nested/fizz/foo.ts"); - assert_contains!(output, "glob/data/test1.ts"); - } - assert_contains!(output, "Found 9 not formatted files in 9 files"); - let cmd_output = context - .new_command() - .args("fmt --check --config deno.glob.json glob/data/test1.?s") - .run(); - - cmd_output.assert_exit_code(1); - - let output = cmd_output.combined_output(); - if cfg!(windows) { - assert_contains!(output, r"glob\data\test1.js"); - assert_contains!(output, r"glob\data\test1.ts"); - } else { - assert_contains!(output, "glob/data/test1.js"); - assert_contains!(output, "glob/data/test1.ts"); - } - - assert_contains!(output, "Found 2 not formatted files in 2 files"); -} diff --git a/cli/tests/integration/info_tests.rs b/cli/tests/integration/info_tests.rs deleted file mode 100644 index 922fcee06..000000000 --- a/cli/tests/integration/info_tests.rs +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::env_vars_for_npm_tests; -use util::TestContextBuilder; - -#[test] -fn info_with_compiled_source() { - let context = TestContextBuilder::new().use_http_server().build(); - let module_path = "http://127.0.0.1:4545/run/048_media_types_jsx.ts"; - - let output = context - .new_command() - .current_dir(util::testdata_path()) - .args_vec(["cache", module_path]) - .run(); - output.assert_exit_code(0); - output.skip_output_check(); - - let output = context - .new_command() - .current_dir(util::testdata_path()) - .args_vec(["info", module_path]) - .split_output() - .run(); - - // check the output of the test.ts program. - assert!(output.stdout().trim().contains("emit: ")); - assert_eq!(output.stderr(), ""); -} - -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", - output: "info/info_json.out", -}); - -itest!(info_json_location { - args: "info --json --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 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 --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_lock { - args: "info main.ts", - http_server: true, - cwd: Some("lockfile/basic"), - exit_code: 10, - output: "lockfile/basic/fail.out", -}); - -itest!(info_no_lock { - args: "info --no-lock main.ts", - http_server: true, - cwd: Some("lockfile/basic"), - output: "lockfile/basic/info.nolock.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", -}); - -itest!(package_json_basic { - args: "info --quiet main.ts", - output: "package_json/basic/main.info.out", - envs: env_vars_for_npm_tests(), - http_server: true, - cwd: Some("package_json/basic"), - copy_temp_dir: Some("package_json/basic"), - exit_code: 0, -}); - -itest!(info_import_map { - args: "info preact/debug", - output: "info/with_import_map/with_import_map.out", - cwd: Some("info/with_import_map"), - exit_code: 0, -}); - -itest!(info_dynamic_imports_tmpl_lit { - args: "info compile/dynamic_imports_tmp_lit/main.js", - output: "compile/dynamic_imports_tmp_lit/main.info.out", - exit_code: 0, -}); diff --git a/cli/tests/integration/init_tests.rs b/cli/tests/integration/init_tests.rs deleted file mode 100644 index d3908eae4..000000000 --- a/cli/tests/integration/init_tests.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::assert_contains; -use util::TestContextBuilder; - -#[test] -fn init_subcommand_without_dir() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let cwd = context.temp_dir().path(); - - let output = context.new_command().args("init").split_output().run(); - - output.assert_exit_code(0); - - let stderr = output.stderr(); - 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!(cwd.join("deno.json").exists()); - - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("run main.ts") - .split_output() - .run(); - - output.assert_exit_code(0); - assert_eq!(output.stdout().as_bytes(), b"Add 2 + 3 = 5\n"); - - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("test") - .split_output() - .run(); - - output.assert_exit_code(0); - assert_contains!(output.stdout(), "1 passed"); - output.skip_output_check(); -} - -#[test] -fn init_subcommand_with_dir_arg() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let cwd = context.temp_dir().path(); - - let output = context - .new_command() - .args("init my_dir") - .split_output() - .run(); - - output.assert_exit_code(0); - - let stderr = output.stderr(); - 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!(cwd.join("my_dir/deno.json").exists()); - - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("run my_dir/main.ts") - .split_output() - .run(); - - output.assert_exit_code(0); - - assert_eq!(output.stdout().as_bytes(), b"Add 2 + 3 = 5\n"); - output.skip_output_check(); - - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("test my_dir/main_test.ts") - .split_output() - .run(); - - output.assert_exit_code(0); - assert_contains!(output.stdout(), "1 passed"); - output.skip_output_check(); -} - -#[test] -fn init_subcommand_with_quiet_arg() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let cwd = context.temp_dir().path(); - - let output = context - .new_command() - .args("init --quiet") - .split_output() - .run(); - - output.assert_exit_code(0); - - assert_eq!(output.stdout(), ""); - assert!(cwd.join("deno.json").exists()); - - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("run main.ts") - .split_output() - .run(); - - output.assert_exit_code(0); - assert_eq!(output.stdout().as_bytes(), b"Add 2 + 3 = 5\n"); - output.skip_output_check(); - - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("test") - .split_output() - .run(); - - output.assert_exit_code(0); - assert_contains!(output.stdout(), "1 passed"); - output.skip_output_check(); -} - -#[test] -fn init_subcommand_with_existing_file() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let cwd = context.temp_dir().path(); - - cwd - .join("main.ts") - .write("console.log('Log from main.ts that already exists');"); - - let output = context.new_command().args("init").split_output().run(); - - output.assert_exit_code(0); - output.assert_stderr_matches_text( - "ℹ️ Skipped creating main.ts as it already exists -✅ Project initialized - -Run these commands to get started - - # Run the program - deno run main.ts - - # Run the program and watch for file changes - deno task dev - - # Run the tests - deno test -", - ); - - assert!(cwd.join("deno.json").exists()); - - let output = context - .new_command() - .env("NO_COLOR", "1") - .args("run main.ts") - .run(); - - output.assert_exit_code(0); - output.assert_matches_text("Log from main.ts that already exists\n"); -} diff --git a/cli/tests/integration/inspector_tests.rs b/cli/tests/integration/inspector_tests.rs deleted file mode 100644 index bbe70ae5e..000000000 --- a/cli/tests/integration/inspector_tests.rs +++ /dev/null @@ -1,1440 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use bytes::Bytes; -use deno_core::anyhow::anyhow; -use deno_core::error::AnyError; -use deno_core::serde_json; -use deno_core::serde_json::json; -use deno_core::url; -use deno_fetch::reqwest; -use fastwebsockets::FragmentCollector; -use fastwebsockets::Frame; -use fastwebsockets::WebSocket; -use hyper::body::Incoming; -use hyper::upgrade::Upgraded; -use hyper::Request; -use hyper::Response; -use hyper_util::rt::TokioIo; -use std::io::BufRead; -use std::time::Duration; -use test_util as util; -use tokio::net::TcpStream; -use tokio::time::timeout; -use url::Url; -use util::assert_starts_with; -use util::DenoChild; -use util::TestContextBuilder; - -struct SpawnExecutor; - -impl<Fut> hyper::rt::Executor<Fut> for SpawnExecutor -where - Fut: std::future::Future + Send + 'static, - Fut::Output: Send + 'static, -{ - fn execute(&self, fut: Fut) { - deno_core::unsync::spawn(fut); - } -} - -async fn connect_to_ws( - uri: Url, -) -> (WebSocket<TokioIo<Upgraded>>, Response<Incoming>) { - let domain = &uri.host().unwrap().to_string(); - let port = &uri.port().unwrap_or(match uri.scheme() { - "wss" | "https" => 443, - _ => 80, - }); - let addr = format!("{domain}:{port}"); - - let stream = TcpStream::connect(addr).await.unwrap(); - - let host = uri.host_str().unwrap(); - - let req = Request::builder() - .method("GET") - .uri(uri.path()) - .header("Host", host) - .header(hyper::header::UPGRADE, "websocket") - .header(hyper::header::CONNECTION, "Upgrade") - .header( - "Sec-WebSocket-Key", - fastwebsockets::handshake::generate_key(), - ) - .header("Sec-WebSocket-Version", "13") - .body(http_body_util::Empty::<Bytes>::new()) - .unwrap(); - - fastwebsockets::handshake::client(&SpawnExecutor, req, stream) - .await - .unwrap() -} - -struct InspectorTester { - socket: FragmentCollector<TokioIo<Upgraded>>, - notification_filter: Box<dyn FnMut(&str) -> bool + 'static>, - child: DenoChild, - stderr_lines: Box<dyn Iterator<Item = String>>, - stdout_lines: Box<dyn Iterator<Item = String>>, -} - -impl Drop for InspectorTester { - fn drop(&mut self) { - _ = self.child.kill(); - } -} - -fn ignore_script_parsed(msg: &str) -> bool { - !msg.starts_with(r#"{"method":"Debugger.scriptParsed","#) -} - -impl InspectorTester { - async fn create<F>(mut child: DenoChild, 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 uri = extract_ws_url_from_stderr(&mut stderr_lines); - - let (socket, response) = connect_to_ws(uri).await; - - assert_eq!(response.status(), 101); // Switching protocols. - - Self { - socket: FragmentCollector::new(socket), - 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 - .write_frame(Frame::text(msg.to_string().into_bytes().into())) - .await - .map_err(|e| anyhow!(e)); - 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: {err:?}.\nstdout:\n{stdout}\nstderr:\n{stderr}" - ); - } - } - } - - async fn recv(&mut self) -> String { - loop { - // In the rare case this locks up, don't wait longer than one minute - let result = timeout(Duration::from_secs(60), self.socket.read_frame()) - .await - .expect("recv() timeout") - .map_err(|e| anyhow!(e)); - let message = - String::from_utf8(self.handle_error(result).payload.to_vec()).unwrap(); - 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.", - ], - ); - } -} - -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; - use std::sync::atomic::Ordering; - static PORT: AtomicU16 = AtomicU16::new(9229); - let port = PORT.fetch_add(1, Ordering::Relaxed); - format!("{flag_prefix}=127.0.0.1:{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") || line.starts_with("Download") { - 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_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 (_socket, response) = connect_to_ws(ws_url).await; - 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) - .piped_output() - .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[Deno.internal].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":"object","subtype":"null","value":null}}}"#], - &[], - ) - .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) - .piped_output() - .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`. We also - // skip this test on Windows because it will occasionally flake, possibly - // due to a similar issue. - if (cfg!(target_os = "linux") - && std::env::var_os("WSL_DISTRO_NAME").is_some()) - || cfg!(windows) - { - 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_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_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) - .piped_output() - .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":{request_id},"result":{{}}}}"#)], - &[r#"{"method":"Debugger.resumed","params":{}}"#], - ) - .await; - } - - // Check that we can gracefully close the websocket connection. - tester - .socket - .write_frame(Frame::close_raw(vec![].into())) - .await - .unwrap(); - - 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) - .piped_output() - .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()) - .piped_output() - .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_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 client = reqwest::Client::new(); - - // Ensure that the webSocketDebuggerUrl matches the host header - for (host, expected) in [ - (None, ws_url.as_str()), - (Some("some.random.host"), "ws://some.random.host/"), - (Some("some.random.host:1234"), "ws://some.random.host:1234/"), - (Some("[::1]:1234"), "ws://[::1]:1234/"), - ] { - let mut req = reqwest::Request::new(reqwest::Method::GET, url.clone()); - if let Some(host) = host { - req.headers_mut().insert( - reqwest::header::HOST, - reqwest::header::HeaderValue::from_static(host), - ); - } - let resp = client.execute(req).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"] - .as_str() - .unwrap() - .contains(expected) - }); - 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_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_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") - .piped_output() - .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"); - let line = tester.stdout_line(); - assert!( - &line.contains("basic test ... ok"), - "Missing content: {line}" - ); - - 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) - .piped_output() - .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) - .piped_output() - .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) - .piped_output() - .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(); -} - -// TODO(bartlomieju): this test became flaky on CI after wiring up "ext/node" -// compatibility layer. Can't reproduce this problem locally for either Mac M1 -// or Linux. Ignoring for now to unblock further integration of "ext/node". -#[ignore] -#[tokio::test] -async fn inspector_break_on_first_line_npm_esm() { - let context = TestContextBuilder::for_npm().build(); - let child = context - .new_command() - .args_vec([ - "run", - "--quiet", - &inspect_flag_with_unique_port("--inspect-brk"), - "npm:@denotest/bin/cli-esm", - "this", - "is", - "a", - "test", - ]) - .spawn_with_piped_output(); - - 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(); -} - -// TODO(bartlomieju): this test became flaky on CI after wiring up "ext/node" -// compatibility layer. Can't reproduce this problem locally for either Mac M1 -// or Linux. Ignoring for now to unblock further integration of "ext/node". -#[ignore] -#[tokio::test] -async fn inspector_break_on_first_line_npm_cjs() { - let context = TestContextBuilder::for_npm().build(); - let child = context - .new_command() - .args_vec([ - "run", - "--quiet", - &inspect_flag_with_unique_port("--inspect-brk"), - "npm:@denotest/bin/cli-cjs", - "this", - "is", - "a", - "test", - ]) - .spawn_with_piped_output(); - - 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(); -} - -// TODO(bartlomieju): this test became flaky on CI after wiring up "ext/node" -// compatibility layer. Can't reproduce this problem locally for either Mac M1 -// or Linux. Ignoring for now to unblock further integration of "ext/node". -#[ignore] -#[tokio::test] -async fn inspector_error_with_npm_import() { - let script = util::testdata_path().join("inspector/error_with_npm_import.js"); - let context = TestContextBuilder::for_npm().build(); - let child = context - .new_command() - .args_vec([ - "run", - "--quiet", - "-A", - &inspect_flag_with_unique_port("--inspect-brk"), - &script.to_string_lossy(), - ]) - .spawn_with_piped_output(); - - 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":"Runtime.exceptionThrown","#], - ) - .await; - 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 test_context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - - let child = test_context - .new_command() - .args_vec([ - "run", - "--quiet", - "-A", - &inspect_flag_with_unique_port("--inspect-wait"), - &script.to_string_lossy(), - ]) - .spawn_with_piped_output(); - - 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 deleted file mode 100644 index 54df82549..000000000 --- a/cli/tests/integration/install_tests.rs +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use test_util::assert_contains; -use util::TestContext; -use util::TestContextBuilder; - -#[test] -fn install_basic() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let temp_dir_str = temp_dir.path().to_string(); - - // ensure a lockfile doesn't get created or updated locally - temp_dir.write("deno.json", "{}"); - - context - .new_command() - .args("install --check --name echo_test http://localhost:4545/echo.ts") - .envs([ - ("HOME", temp_dir_str.as_str()), - ("USERPROFILE", temp_dir_str.as_str()), - ("DENO_INSTALL_ROOT", ""), - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - // 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 = file_path.read_to_string(); - // 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 - context - .new_command() - .args("uninstall echo_test") - .envs([ - ("HOME", temp_dir_str.as_str()), - ("USERPROFILE", temp_dir_str.as_str()), - ("DENO_INSTALL_ROOT", ""), - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - // 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 context = TestContext::with_http_server(); - let temp_dir = context.temp_dir(); - let temp_dir_str = temp_dir.path().to_string(); - - context - .new_command() - .current_dir(util::root_path()) // different cwd - .args("install --check --name echo_test 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()), - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - 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 = file_path.read_to_string(); - 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 context = TestContext::with_http_server(); - let temp_dir = context.temp_dir(); - let temp_dir_str = temp_dir.path().to_string(); - let echo_ts_str = util::testdata_path().join("echo.ts").to_string(); - - context - .new_command() - .current_dir(util::root_path()) - .args_vec([ - "install", - "--name", - "echo_test", - "--root", - temp_dir_str.as_str(), - echo_ts_str.as_str(), - "hello", - ]) - .envs([ - ("HOME", temp_dir_str.as_str()), - ("USERPROFILE", temp_dir_str.as_str()), - ("DENO_INSTALL_ROOT", ""), - ]) - .run() - .skip_output_check() - .assert_exit_code(0); - - let bin_dir = temp_dir.path().join("bin"); - 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 = context - .new_command() - .name(&file_path) - .current_dir(temp_dir.path()) - .args("foo") - .env("PATH", util::target_dir()) - .run(); - output.assert_matches_text("hello, foo"); - output.assert_exit_code(0); -} - -#[test] -fn installer_test_remote_module_run() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let root_dir = temp_dir.path().join("root"); - let bin_dir = root_dir.join("bin"); - context - .new_command() - .args("install --name echo_test --root ./root http://localhost:4545/echo.ts hello") - .run() - .skip_output_check() - .assert_exit_code(0); - let mut bin_file_path = bin_dir.join("echo_test"); - if cfg!(windows) { - bin_file_path = bin_file_path.with_extension("cmd"); - } - assert!(bin_file_path.exists()); - let output = context - .new_command() - .name(&bin_file_path) - .current_dir(root_dir) - .args("foo") - .env("PATH", util::target_dir()) - .run(); - output.assert_matches_text("hello, foo"); - output.assert_exit_code(0); - - // now uninstall with the relative path - context - .new_command() - .args("uninstall --root ./root echo_test") - .run() - .skip_output_check() - .assert_exit_code(0); - assert!(!bin_file_path.exists()); -} - -#[test] -fn check_local_by_default() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let temp_dir_str = temp_dir.path().to_string(); - let script_path = - util::testdata_path().join("./install/check_local_by_default.ts"); - let script_path_str = script_path.to_string_lossy().to_string(); - context - .new_command() - .args_vec(["install", script_path_str.as_str()]) - .envs([ - ("HOME", temp_dir_str.as_str()), - ("USERPROFILE", temp_dir_str.as_str()), - ("DENO_INSTALL_ROOT", ""), - ]) - .run() - .skip_output_check() - .assert_exit_code(0); -} - -#[test] -fn check_local_by_default2() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let temp_dir_str = temp_dir.path().to_string(); - let script_path = - util::testdata_path().join("./install/check_local_by_default2.ts"); - let script_path_str = script_path.to_string_lossy().to_string(); - context - .new_command() - .args_vec(["install", script_path_str.as_str()]) - .envs([ - ("HOME", temp_dir_str.as_str()), - ("NO_COLOR", "1"), - ("USERPROFILE", temp_dir_str.as_str()), - ("DENO_INSTALL_ROOT", ""), - ]) - .run() - .skip_output_check() - .assert_exit_code(0); -} diff --git a/cli/tests/integration/js_unit_tests.rs b/cli/tests/integration/js_unit_tests.rs deleted file mode 100644 index 0e3a1a118..000000000 --- a/cli/tests/integration/js_unit_tests.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use std::io::BufRead; -use std::io::BufReader; -use std::time::Duration; -use std::time::Instant; -use test_util as util; - -util::unit_test_factory!( - js_unit_test, - "../tests/unit", - "*.ts", - [ - abort_controller_test, - blob_test, - body_test, - broadcast_channel_test, - buffer_test, - build_test, - cache_api_test, - chmod_test, - chown_test, - command_test, - console_test, - copy_file_test, - custom_event_test, - cron_test, - dir_test, - dom_exception_test, - error_stack_test, - error_test, - esnext_test, - event_target_test, - event_test, - fetch_test, - ffi_test, - file_test, - filereader_test, - files_test, - flock_test, - fs_events_test, - get_random_values_test, - globals_test, - headers_test, - http_test, - image_bitmap_test, - image_data_test, - internals_test, - intl_test, - io_test, - jupyter_test, - kv_test, - kv_queue_test_no_db_close, - kv_queue_test, - kv_queue_undelivered_test, - link_test, - make_temp_test, - message_channel_test, - mkdir_test, - navigator_test, - net_test, - network_interfaces_test, - os_test, - ops_test, - path_from_url_test, - performance_test, - permissions_test, - process_test, - progressevent_test, - promise_hooks_test, - read_dir_test, - read_file_test, - read_link_test, - read_text_file_test, - real_path_test, - ref_unref_test, - remove_test, - rename_test, - request_test, - resources_test, - response_test, - serve_test, - signal_test, - stat_test, - stdio_test, - streams_test, - structured_clone_test, - symbol_test, - symlink_test, - sync_test, - test_util, - testing_test, - text_encoding_test, - timers_test, - tls_test, - truncate_test, - tty_color_test, - tty_test, - umask_test, - url_search_params_test, - url_test, - urlpattern_test, - utime_test, - version_test, - wasm_test, - webcrypto_test, - webgpu_test, - websocket_test, - webstorage_test, - worker_permissions_test, - worker_test, - write_file_test, - write_text_file_test, - ] -); - -fn js_unit_test(test: String) { - let _g = util::http_server(); - - let deno = util::deno_cmd() - .current_dir(util::root_path()) - .arg("test") - .arg("--config") - .arg("cli/tests/config/deno.json") - .arg("--no-lock") - .arg("--unstable") - .arg("--location=http://127.0.0.1:4545/") - .arg("--no-prompt"); - - // TODO(mmastrac): it would be better to just load a test CA for all tests - let deno = if test == "websocket_test" { - deno.arg("--unsafely-ignore-certificate-errors") - } else { - deno - }; - - let mut deno = deno - .arg("-A") - .arg(util::tests_path().join("unit").join(format!("{test}.ts"))) - .piped_output() - .spawn() - .expect("failed to spawn script"); - - let now = Instant::now(); - let stdout = deno.stdout.take().unwrap(); - let test_name = test.clone(); - let stdout = std::thread::spawn(move || { - let reader = BufReader::new(stdout); - for line in reader.lines() { - if let Ok(line) = line { - println!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); - } else { - break; - } - } - }); - - let now = Instant::now(); - let stderr = deno.stderr.take().unwrap(); - let test_name = test.clone(); - let stderr = std::thread::spawn(move || { - let reader = BufReader::new(stderr); - for line in reader.lines() { - if let Ok(line) = line { - eprintln!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); - } else { - break; - } - } - }); - - const PER_TEST_TIMEOUT: Duration = Duration::from_secs(3 * 60); - - let now = Instant::now(); - let status = loop { - if now.elapsed() > PER_TEST_TIMEOUT { - // Last-ditch kill - _ = deno.kill(); - panic!("Test {test} failed to complete in time"); - } - if let Some(status) = deno - .try_wait() - .expect("failed to wait for the child process") - { - break status; - } - std::thread::sleep(Duration::from_millis(100)); - }; - - #[cfg(unix)] - assert_eq!( - std::os::unix::process::ExitStatusExt::signal(&status), - None, - "Deno should not have died with a signal" - ); - assert_eq!(Some(0), status.code(), "Deno should have exited cleanly"); - - stdout.join().unwrap(); - stderr.join().unwrap(); - - assert!(status.success()); -} diff --git a/cli/tests/integration/jsr_tests.rs b/cli/tests/integration/jsr_tests.rs deleted file mode 100644 index 2de4f0056..000000000 --- a/cli/tests/integration/jsr_tests.rs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::serde_json::Value; -use deno_lockfile::Lockfile; -use test_util as util; -use url::Url; -use util::env_vars_for_jsr_tests; -use util::TestContextBuilder; - -itest!(no_module_graph_run { - args: "run jsr/no_module_graph/main.ts", - output: "jsr/no_module_graph/main.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(no_module_graph_info { - args: "info jsr/no_module_graph/main.ts", - output: "jsr/no_module_graph/main_info.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(same_package_multiple_versions { - args: "run --quiet jsr/no_module_graph/multiple.ts", - output: "jsr/no_module_graph/multiple.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(module_graph_run { - args: "run jsr/module_graph/main.ts", - output: "jsr/module_graph/main.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(module_graph_info { - args: "info jsr/module_graph/main.ts", - output: "jsr/module_graph/main_info.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(deps_run { - args: "run jsr/deps/main.ts", - output: "jsr/deps/main.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(deps_info { - args: "info jsr/deps/main.ts", - output: "jsr/deps/main_info.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(subset_type_graph { - args: "check --all jsr/subset_type_graph/main.ts", - output: "jsr/subset_type_graph/main.check.out", - envs: env_vars_for_jsr_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(version_not_found { - args: "run jsr/version_not_found/main.ts", - output: "jsr/version_not_found/main.out", - envs: env_vars_for_jsr_tests(), - http_server: true, - exit_code: 1, -}); - -#[test] -fn specifiers_in_lockfile() { - let test_context = TestContextBuilder::for_jsr().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - - temp_dir.write( - "main.ts", - r#"import version from "jsr:@denotest/no_module_graph@0.1"; - -console.log(version);"#, - ); - temp_dir.write("deno.json", "{}"); // to automatically create a lockfile - - test_context - .new_command() - .args("run --quiet main.ts") - .run() - .assert_matches_text("0.1.1\n"); - - let lockfile_path = temp_dir.path().join("deno.lock"); - let mut lockfile = Lockfile::new(lockfile_path.to_path_buf(), false).unwrap(); - *lockfile - .content - .packages - .specifiers - .get_mut("jsr:@denotest/no_module_graph@0.1") - .unwrap() = "jsr:@denotest/no_module_graph@0.1.0".to_string(); - lockfile_path.write(lockfile.as_json_string()); - - test_context - .new_command() - .args("run --quiet main.ts") - .run() - .assert_matches_text("0.1.0\n"); -} - -#[test] -fn reload_info_not_found_cache_but_exists_remote() { - fn remove_version(registry_json: &mut Value, version: &str) { - registry_json - .as_object_mut() - .unwrap() - .get_mut("versions") - .unwrap() - .as_object_mut() - .unwrap() - .remove(version); - } - - fn remove_version_for_package( - deno_dir: &util::TempDir, - package: &str, - version: &str, - ) { - let specifier = - Url::parse(&format!("http://127.0.0.1:4250/{}/meta.json", package)) - .unwrap(); - let registry_json_path = deno_dir - .path() - .join("deps") - .join(deno_cache_dir::url_to_filename(&specifier).unwrap()); - let mut registry_json = registry_json_path.read_json_value(); - remove_version(&mut registry_json, version); - registry_json_path.write_json(®istry_json); - } - - // This tests that when a local machine doesn't have a version - // specified in a dependency that exists in the npm registry - let test_context = TestContextBuilder::for_jsr().use_temp_cwd().build(); - let deno_dir = test_context.deno_dir(); - let temp_dir = test_context.temp_dir(); - temp_dir.write( - "main.ts", - "import { add } from 'jsr:@denotest/add@1'; console.log(add(1, 2));", - ); - - // cache successfully to the deno_dir - let output = test_context.new_command().args("cache main.ts").run(); - output.assert_matches_text(concat!( - "Download http://127.0.0.1:4250/@denotest/add/meta.json\n", - "Download http://127.0.0.1:4250/@denotest/add/1.0.0_meta.json\n", - "Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts\n", - )); - - // modify the package information in the cache to remove the latest version - remove_version_for_package(deno_dir, "@denotest/add", "1.0.0"); - - // should error when `--cache-only` is used now because the version is not in the cache - let output = test_context - .new_command() - .args("run --cached-only main.ts") - .run(); - output.assert_exit_code(1); - output.assert_matches_text("error: Failed to resolve version constraint. Try running again without --cached-only - at file:///[WILDCARD]main.ts:1:21 -"); - - // now try running without it, it should download the package now - test_context - .new_command() - .args("run main.ts") - .run() - .assert_matches_text(concat!( - "Download http://127.0.0.1:4250/@denotest/add/meta.json\n", - "Download http://127.0.0.1:4250/@denotest/add/1.0.0_meta.json\n", - "3\n", - )) - .assert_exit_code(0); -} diff --git a/cli/tests/integration/jupyter_tests.rs b/cli/tests/integration/jupyter_tests.rs deleted file mode 100644 index 59c247e5d..000000000 --- a/cli/tests/integration/jupyter_tests.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -itest!(jupyter_install_command_not_exists { - args: "jupyter --install", - output: "jupyter/install_command_not_exists.out", - envs: vec![("PATH".to_string(), "".to_string())], - exit_code: 1, -}); diff --git a/cli/tests/integration/lint_tests.rs b/cli/tests/integration/lint_tests.rs deleted file mode 100644 index b266fb5b7..000000000 --- a/cli/tests/integration/lint_tests.rs +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util::assert_contains; -use test_util::TestContextBuilder; - -itest!(ignore_unexplicit_files { - args: "lint --ignore=./", - output_str: Some("error: No target files found.\n"), - exit_code: 1, -}); - -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, -}); - -#[test] -fn lint_with_glob_config() { - let context = TestContextBuilder::new().cwd("lint").build(); - - let cmd_output = context - .new_command() - .args("lint --config deno.glob.json") - .run(); - - cmd_output.assert_exit_code(1); - - let output = cmd_output.combined_output(); - if cfg!(windows) { - assert_contains!(output, r"glob\nested\fizz\fizz.ts:1:10"); - assert_contains!(output, r"glob\pages\[id].ts:1:10"); - assert_contains!(output, r"glob\nested\fizz\bar.ts:1:10"); - assert_contains!(output, r"glob\nested\foo\foo.ts:1:10"); - assert_contains!(output, r"glob\data\test1.js:1:10"); - assert_contains!(output, r"glob\nested\foo\bar.ts:1:10"); - assert_contains!(output, r"glob\nested\foo\fizz.ts:1:10"); - assert_contains!(output, r"glob\nested\fizz\foo.ts:1:10"); - assert_contains!(output, r"glob\data\test1.ts:1:10"); - } else { - assert_contains!(output, "glob/nested/fizz/fizz.ts:1:10"); - assert_contains!(output, "glob/pages/[id].ts:1:10"); - assert_contains!(output, "glob/nested/fizz/bar.ts:1:10"); - assert_contains!(output, "glob/nested/foo/foo.ts:1:10"); - assert_contains!(output, "glob/data/test1.js:1:10"); - assert_contains!(output, "glob/nested/foo/bar.ts:1:10"); - assert_contains!(output, "glob/nested/foo/fizz.ts:1:10"); - assert_contains!(output, "glob/nested/fizz/foo.ts:1:10"); - assert_contains!(output, "glob/data/test1.ts:1:10"); - } - assert_contains!(output, "Found 9 problems"); - assert_contains!(output, "Checked 9 files"); -} - -#[test] -fn lint_with_glob_config_and_flags() { - let context = TestContextBuilder::new().cwd("lint").build(); - - let cmd_output = context - .new_command() - .args("lint --config deno.glob.json --ignore=glob/nested/**/bar.ts") - .run(); - - cmd_output.assert_exit_code(1); - - let output = cmd_output.combined_output(); - if cfg!(windows) { - assert_contains!(output, r"glob\nested\fizz\fizz.ts:1:10"); - assert_contains!(output, r"glob\pages\[id].ts:1:10"); - assert_contains!(output, r"glob\nested\fizz\bazz.ts:1:10"); - assert_contains!(output, r"glob\nested\foo\foo.ts:1:10"); - assert_contains!(output, r"glob\data\test1.js:1:10"); - assert_contains!(output, r"glob\nested\foo\bazz.ts:1:10"); - assert_contains!(output, r"glob\nested\foo\fizz.ts:1:10"); - assert_contains!(output, r"glob\nested\fizz\foo.ts:1:10"); - assert_contains!(output, r"glob\data\test1.ts:1:10"); - } else { - assert_contains!(output, "glob/nested/fizz/fizz.ts:1:10"); - assert_contains!(output, "glob/pages/[id].ts:1:10"); - assert_contains!(output, "glob/nested/fizz/bazz.ts:1:10"); - assert_contains!(output, "glob/nested/foo/foo.ts:1:10"); - assert_contains!(output, "glob/data/test1.js:1:10"); - assert_contains!(output, "glob/nested/foo/bazz.ts:1:10"); - assert_contains!(output, "glob/nested/foo/fizz.ts:1:10"); - assert_contains!(output, "glob/nested/fizz/foo.ts:1:10"); - assert_contains!(output, "glob/data/test1.ts:1:10"); - } - assert_contains!(output, "Found 9 problems"); - assert_contains!(output, "Checked 9 files"); - - let cmd_output = context - .new_command() - .args("lint --config deno.glob.json glob/data/test1.?s") - .run(); - - cmd_output.assert_exit_code(1); - - let output = cmd_output.combined_output(); - if cfg!(windows) { - assert_contains!(output, r"glob\data\test1.js:1:10"); - assert_contains!(output, r"glob\data\test1.ts:1:10"); - } else { - assert_contains!(output, "glob/data/test1.js:1:10"); - assert_contains!(output, "glob/data/test1.ts:1:10"); - } - assert_contains!(output, "Found 2 problems"); - assert_contains!(output, "Checked 2 files"); -} diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs deleted file mode 100644 index c9abae241..000000000 --- a/cli/tests/integration/lsp_tests.rs +++ /dev/null @@ -1,11240 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_ast::ModuleSpecifier; -use deno_core::serde::Deserialize; -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::fs; -use test_util::assert_starts_with; -use test_util::deno_cmd_with_deno_dir; -use test_util::env_vars_for_npm_tests; -use test_util::lsp::LspClient; -use test_util::testdata_path; -use test_util::TestContextBuilder; -use tower_lsp::lsp_types as lsp; - -#[test] -fn lsp_startup_shutdown() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.shutdown(); -} - -#[test] -fn lsp_init_tsconfig() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - temp_dir.write( - "lib.tsconfig.json", - r#"{ - "compilerOptions": { - "lib": ["deno.ns", "deno.unstable", "dom"] - } -}"#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("lib.tsconfig.json"); - }); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "location.pathname;\n" - } - })); - - assert_eq!(diagnostics.all().len(), 0); - - client.shutdown(); -} - -#[test] -fn lsp_tsconfig_types() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - temp_dir.write( - "types.tsconfig.json", - r#"{ - "compilerOptions": { - "types": ["./a.d.ts"] - }, - "lint": { - "rules": { - "tags": [] - } - } -}"#, - ); - let a_dts = "// deno-lint-ignore-file no-var\ndeclare var a: string;"; - temp_dir.write("a.d.ts", a_dts); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("types.tsconfig.json"); - }); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": Url::from_file_path(temp_dir.path().join("test.ts")).unwrap(), - "languageId": "typescript", - "version": 1, - "text": "console.log(a);\n" - } - })); - - assert_eq!(diagnostics.all().len(), 0); - - client.shutdown(); -} - -#[test] -fn lsp_tsconfig_bad_config_path() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder - .set_config("bad_tsconfig.json") - .set_maybe_root_uri(None); - }); - let (method, maybe_params) = client.read_notification(); - 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 = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Deno.args);\n" - } - })); - assert_eq!(diagnostics.all().len(), 0); -} - -#[test] -fn lsp_triple_slash_types() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - let a_dts = "// deno-lint-ignore-file no-var\ndeclare var a: string;"; - temp_dir.write("a.d.ts", a_dts); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("test.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "/// <reference types=\"./a.d.ts\" />\n\nconsole.log(a);\n" - } - })); - - assert_eq!(diagnostics.all().len(), 0); - - client.shutdown(); -} - -#[test] -fn lsp_import_map() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - let import_map = r#"{ - "imports": { - "/~/": "./lib/" - } -}"#; - temp_dir.write("import-map.json", import_map); - temp_dir.create_dir_all("lib"); - temp_dir.write("lib/b.ts", r#"export const b = "b";"#); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_import_map("import-map.json"); - }); - - let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap(); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n" - } - })); - - assert_eq!(diagnostics.all().len(), 0); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": uri - }, - "position": { "line": 2, "character": 12 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value":"(alias) const b: \"b\"\nimport b" - }, - "" - ], - "range": { - "start": { "line": 2, "character": 12 }, - "end": { "line": 2, "character": 13 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_import_map_remote() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.json", - json!({ - "importMap": "http://localhost:4545/import_maps/import_map.json", - }) - .to_string(), - ); - temp_dir.write( - "file.ts", - r#" - import { printHello } from "print_hello"; - printHello(); - "#, - ); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_import_map("http://localhost:4545/import_maps/import_map.json"); - }); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [[], temp_dir.uri().join("file.ts").unwrap()], - }), - ); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("file.ts"), - } - })); - assert_eq!(diagnostics.all(), vec![]); - client.shutdown(); -} - -#[test] -fn lsp_import_map_data_url() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}"); - }); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import example from \"example\";\n" - } - })); - - // This indicates that the import map is applied correctly. - assert!(diagnostics.all().iter().any(|diagnostic| diagnostic.code - == Some(lsp::NumberOrString::String("no-cache".to_string())) - && diagnostic - .message - .contains("https://deno.land/x/example/mod.ts"))); - client.shutdown(); -} - -#[test] -fn lsp_import_map_config_file() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.import_map.jsonc", - r#"{ - "importMap": "import-map.json" -}"#, - ); - temp_dir.write( - "import-map.json", - r#"{ - "imports": { - "/~/": "./lib/" - } -}"#, - ); - temp_dir.create_dir_all("lib"); - temp_dir.write("lib/b.ts", r#"export const b = "b";"#); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.import_map.jsonc"); - }); - - let uri = temp_dir.uri().join("a.ts").unwrap(); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n" - } - })); - - assert_eq!(diagnostics.all().len(), 0); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": uri - }, - "position": { "line": 2, "character": 12 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value":"(alias) const b: \"b\"\nimport b" - }, - "" - ], - "range": { - "start": { "line": 2, "character": 12 }, - "end": { "line": 2, "character": 13 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_import_map_embedded_in_config_file() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.embedded_import_map.jsonc", - r#"{ - "imports": { - "/~/": "./lib/" - } -}"#, - ); - temp_dir.create_dir_all("lib"); - temp_dir.write("lib/b.ts", r#"export const b = "b";"#); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.embedded_import_map.jsonc"); - }); - - let uri = temp_dir.uri().join("a.ts").unwrap(); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n" - } - })); - - assert_eq!(diagnostics.all().len(), 0); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": uri - }, - "position": { "line": 2, "character": 12 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value":"(alias) const b: \"b\"\nimport b" - }, - "" - ], - "range": { - "start": { "line": 2, "character": 12 }, - "end": { "line": 2, "character": 13 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_import_map_embedded_in_config_file_after_initialize() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("deno.embedded_import_map.jsonc", "{}"); - temp_dir.create_dir_all("lib"); - temp_dir.write("lib/b.ts", r#"export const b = "b";"#); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.embedded_import_map.jsonc"); - }); - - let uri = temp_dir.uri().join("a.ts").unwrap(); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n" - } - })); - - assert_eq!(diagnostics.all().len(), 1); - - // update the import map - temp_dir.write( - "deno.embedded_import_map.jsonc", - r#"{ - "imports": { - "/~/": "./lib/" - } -}"#, - ); - - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.embedded_import_map.jsonc").unwrap(), - "type": 2 - }] - })); - - assert_eq!(client.read_diagnostics().all().len(), 0); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": uri - }, - "position": { "line": 2, "character": 12 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value":"(alias) const b: \"b\"\nimport b" - }, - "" - ], - "range": { - "start": { "line": 2, "character": 12 }, - "end": { "line": 2, "character": 13 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_import_map_config_file_auto_discovered() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.create_dir_all("lib"); - temp_dir.write("lib/b.ts", r#"export const b = "b";"#); - - let mut client = context.new_lsp_command().capture_stderr().build(); - client.initialize_default(); - - // add the deno.json - temp_dir.write("deno.jsonc", r#"{ "imports": { "/~/": "./lib/" } }"#); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.jsonc").unwrap(), - "type": 2 - }] - })); - client.wait_until_stderr_line(|line| { - line.contains("Auto-resolved configuration file:") - }); - - let uri = temp_dir.uri().join("a.ts").unwrap(); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n" - } - })); - - assert_eq!(diagnostics.all().len(), 0); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": uri - }, - "position": { "line": 2, "character": 12 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value":"(alias) const b: \"b\"\nimport b" - }, - "" - ], - "range": { - "start": { "line": 2, "character": 12 }, - "end": { "line": 2, "character": 13 } - } - }) - ); - - // now cause a syntax error - temp_dir.write("deno.jsonc", r#",,#,#,,"#); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.jsonc").unwrap(), - "type": 2 - }] - })); - assert_eq!(client.read_diagnostics().all().len(), 1); - - // now fix it, and things should work again - temp_dir.write("deno.jsonc", r#"{ "imports": { "/~/": "./lib/" } }"#); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.jsonc").unwrap(), - "type": 2 - }] - })); - client.wait_until_stderr_line(|line| { - line.contains("Auto-resolved configuration file:") - }); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": uri - }, - "position": { "line": 2, "character": 12 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value":"(alias) const b: \"b\"\nimport b" - }, - "" - ], - "range": { - "start": { "line": 2, "character": 12 }, - "end": { "line": 2, "character": 13 } - } - }) - ); - assert_eq!(client.read_diagnostics().all().len(), 0); - - client.shutdown(); -} - -#[test] -fn lsp_import_map_config_file_auto_discovered_symlink() { - let context = TestContextBuilder::new() - // DO NOT COPY THIS CODE. Very rare case where we want to force the temp - // directory on the CI to not be a symlinked directory because we are - // testing a scenario with a symlink to a non-symlink in the same directory - // tree. Generally you want to ensure your code works in symlinked directories - // so don't use this unless you have a similar scenario. - .temp_dir_path(std::env::temp_dir().canonicalize().unwrap()) - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - temp_dir.create_dir_all("lib"); - temp_dir.write("lib/b.ts", r#"export const b = "b";"#); - - let mut client = context.new_lsp_command().capture_stderr().build(); - client.initialize_default(); - - // now create a symlink in the current directory to a subdir/deno.json - // and ensure the watched files notification still works - temp_dir.create_dir_all("subdir"); - temp_dir.write("subdir/deno.json", r#"{ }"#); - temp_dir.symlink_file( - temp_dir.path().join("subdir").join("deno.json"), - temp_dir.path().join("deno.json"), - ); - client.did_change_watched_files(json!({ - "changes": [{ - // the client will give a watched file changed event for the symlink's target - "uri": temp_dir.path().join("subdir/deno.json").canonicalize().uri_file(), - "type": 2 - }] - })); - - // this will discover the deno.json in the root - let search_line = format!( - "Auto-resolved configuration file: \"{}\"", - temp_dir.uri().join("deno.json").unwrap().as_str() - ); - client.wait_until_stderr_line(|line| line.contains(&search_line)); - - // now open a file which will cause a diagnostic because the import map is empty - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("a.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n" - } - })); - assert_eq!(diagnostics.all().len(), 1); - - // update the import map to have the imports now - temp_dir.write("subdir/deno.json", r#"{ "imports": { "/~/": "./lib/" } }"#); - client.did_change_watched_files(json!({ - "changes": [{ - // now still say that the target path has changed - "uri": temp_dir.path().join("subdir/deno.json").canonicalize().uri_file(), - "type": 2 - }] - })); - assert_eq!(client.read_diagnostics().all().len(), 0); - - client.shutdown(); -} - -#[test] -fn lsp_import_map_node_specifiers() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - temp_dir.write("deno.json", r#"{ "imports": { "fs": "node:fs" } }"#); - - // cache @types/node - context - .new_command() - .args("cache npm:@types/node") - .run() - .skip_output_check() - .assert_exit_code(0); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.json"); - }); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("a.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "import fs from \"fs\";\nconsole.log(fs);" - } - })); - assert_eq!(diagnostics.all(), vec![]); - - client.shutdown(); -} - -#[test] -fn lsp_format_vendor_path() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - temp_dir.write("deno.json", json!({ "vendor": true }).to_string()); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": r#"import "http://localhost:4545/run/002_hello.ts";"#, - }, - })); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [[], "file:///a/file.ts"], - }), - ); - assert!(temp_dir - .path() - .join("vendor/http_localhost_4545/run/002_hello.ts") - .exists()); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("vendor/http_localhost_4545/run/002_hello.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": r#"console.log("Hello World");"#, - }, - })); - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": temp_dir.uri().join("vendor/http_localhost_4545/run/002_hello.ts").unwrap(), - }, - "options": { - "tabSize": 2, - "insertSpaces": true, - } - }), - ); - assert_eq!( - res, - json!([{ - "range": { - "start": { - "line": 0, - "character": 27, - }, - "end": { - "line": 0, - "character": 27, - }, - }, - "newText": "\n", - }]), - ); - client.shutdown(); -} - -// Regression test for https://github.com/denoland/deno/issues/19802. -// Disable the `workspace/configuration` capability. Ensure the LSP falls back -// to using `enablePaths` from the `InitializationOptions`. -#[test] -fn lsp_workspace_enable_paths_no_workspace_configuration() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("main_disabled.ts", "Date.now()"); - temp_dir.write("main_enabled.ts", "Date.now()"); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.with_capabilities(|capabilities| { - capabilities.workspace.as_mut().unwrap().configuration = Some(false); - }); - builder.set_workspace_folders(vec![lsp::WorkspaceFolder { - uri: temp_dir.uri(), - name: "project".to_string(), - }]); - builder.set_root_uri(temp_dir.uri()); - builder.set_enable_paths(vec!["./main_enabled.ts".to_string()]); - }); - - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("main_disabled.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("main_disabled.ts"), - } - })); - - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("main_enabled.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("main_enabled.ts"), - } - })); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": temp_dir.uri().join("main_disabled.ts").unwrap(), - }, - "position": { "line": 0, "character": 5 } - }), - ); - assert_eq!(res, json!(null)); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": temp_dir.uri().join("main_enabled.ts").unwrap(), - }, - "position": { "line": 0, "character": 5 } - }), - ); - assert_eq!( - res, - 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": 5, }, - "end": { "line": 0, "character": 8, } - } - }) - ); - - client.shutdown(); -} - -#[test] -fn lsp_did_change_deno_configuration_notification() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - temp_dir.write("deno.json", json!({}).to_string()); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.json").unwrap(), - "type": 1, - }], - })); - let res = client - .read_notification_with_method::<Value>("deno/didChangeDenoConfiguration"); - assert_eq!( - res, - Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("deno.json").unwrap(), - "type": "added", - "configurationType": "denoJson" - }], - })) - ); - - temp_dir.write( - "deno.json", - json!({ "fmt": { "semiColons": false } }).to_string(), - ); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.json").unwrap(), - "type": 2, - }], - })); - let res = client - .read_notification_with_method::<Value>("deno/didChangeDenoConfiguration"); - assert_eq!( - res, - Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("deno.json").unwrap(), - "type": "changed", - "configurationType": "denoJson" - }], - })) - ); - - temp_dir.remove_file("deno.json"); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.json").unwrap(), - "type": 3, - }], - })); - let res = client - .read_notification_with_method::<Value>("deno/didChangeDenoConfiguration"); - assert_eq!( - res, - Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("deno.json").unwrap(), - "type": "removed", - "configurationType": "denoJson" - }], - })) - ); - - temp_dir.write("package.json", json!({}).to_string()); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("package.json").unwrap(), - "type": 1, - }], - })); - let res = client - .read_notification_with_method::<Value>("deno/didChangeDenoConfiguration"); - assert_eq!( - res, - Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("package.json").unwrap(), - "type": "added", - "configurationType": "packageJson" - }], - })) - ); - - temp_dir.write("package.json", json!({ "type": "module" }).to_string()); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("package.json").unwrap(), - "type": 2, - }], - })); - let res = client - .read_notification_with_method::<Value>("deno/didChangeDenoConfiguration"); - assert_eq!( - res, - Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("package.json").unwrap(), - "type": "changed", - "configurationType": "packageJson" - }], - })) - ); - - temp_dir.remove_file("package.json"); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("package.json").unwrap(), - "type": 3, - }], - })); - let res = client - .read_notification_with_method::<Value>("deno/didChangeDenoConfiguration"); - assert_eq!( - res, - Some(json!({ - "changes": [{ - "scopeUri": temp_dir.uri(), - "fileUri": temp_dir.uri().join("package.json").unwrap(), - "type": "removed", - "configurationType": "packageJson" - }], - })) - ); -} - -#[test] -fn lsp_deno_task() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.jsonc", - r#"{ - "tasks": { - "build": "deno test", - "some:test": "deno bundle mod.ts" - } - }"#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.jsonc"); - }); - - let res = client.write_request("deno/task", json!(null)); - - assert_eq!( - res, - json!([ - { - "name": "build", - "detail": "deno test", - "sourceUri": temp_dir.uri().join("deno.jsonc").unwrap(), - }, { - "name": "some:test", - "detail": "deno bundle mod.ts", - "sourceUri": temp_dir.uri().join("deno.jsonc").unwrap(), - } - ]) - ); -} - -#[test] -fn lsp_reload_import_registries_command() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let res = client.write_request( - "workspace/executeCommand", - json!({ "command": "deno.reloadImportRegistries" }), - ); - assert_eq!(res, json!(true)); -} - -#[test] -fn lsp_import_attributes() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}"); - }); - client.change_configuration(json!({ - "deno": { - "enable": true, - "codeLens": { - "test": true, - }, - }, - })); - - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/test.json", - "languageId": "json", - "version": 1, - "text": "{\"a\":1}", - }, - })); - - let diagnostics = client.did_open(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 - .messages_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-attribute-type", - "source": "deno", - "message": "The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \"json\" }` to the import statement." - } - ]) - ); - - let res = client - .write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/a.ts" - }, - "range": { - "start": { "line": 0, "character": 14 }, - "end": { "line": 0, "character": 27 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 14 }, - "end": { "line": 0, "character": 27 } - }, - "severity": 1, - "code": "no-attribute-type", - "source": "deno", - "message": "The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \"json\" }` to the import statement." - }], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Insert import attribute.", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 14 }, - "end": { "line": 0, "character": 27 } - }, - "severity": 1, - "code": "no-attribute-type", - "source": "deno", - "message": "The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \"json\" }` to the import statement." - } - ], - "edit": { - "changes": { - "file:///a/a.ts": [ - { - "range": { - "start": { "line": 0, "character": 27 }, - "end": { "line": 0, "character": 27 } - }, - "newText": " with { type: \"json\" }" - } - ] - } - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_import_map_import_completions() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "import-map.json", - r#"{ - "imports": { - "/~/": "./lib/", - "fs": "https://example.com/fs/index.js", - "std/": "https://example.com/std@0.123.0/" - } -}"#, - ); - temp_dir.create_dir_all("lib"); - temp_dir.write("lib/b.ts", r#"export const b = "b";"#); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_import_map("import-map.json"); - }); - - let uri = temp_dir.uri().join("a.ts").unwrap(); - - client.did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"/~/b.ts\";\nimport * as b from \"\"" - } - })); - - let res = client.get_completion( - &uri, - (1, 20), - json!({ - "triggerKind": 2, - "triggerCharacter": "\"" - }), - ); - assert_eq!( - json!(res), - 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": "/~/" - } - ] - }), - ); - - let res = client.get_completion( - uri, - (1, 23), - json!({ - "triggerKind": 2, - "triggerCharacter": "/" - }), - ); - assert_eq!( - json!(res), - 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": ["\"", "'"], - } - ] - }) - ); - - client.shutdown(); -} - -#[test] -fn lsp_hover() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Deno.args);\n" - } - })); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!( - res, - 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://examples.deno.land/command-line-arguments.ts Sushi\n```\n\nThen `Deno.args` will contain:\n\n```ts\n[ \"Sushi\" ]\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 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_asset() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - })); - client.write_request( - "textDocument/definition", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 14 } - }), - ); - client.write_request( - "deno/virtualTextDocument", - json!({ - "textDocument": { - "uri": "deno:/asset/lib.deno.shared_globals.d.ts" - } - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "deno:/asset/lib.es2015.symbol.wellknown.d.ts" - }, - "position": { "line": 111, "character": 13 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value": "interface Date", - }, - "Enables basic storage and retrieval of dates and times." - ], - "range": { - "start": { "line": 111, "character": 10, }, - "end": { "line": 111, "character": 14, } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_disabled() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_deno_enable(false); - }); - client.change_configuration(json!({ "deno": { "enable": false } })); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n", - }, - })); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!(res, json!(null)); - client.shutdown(); -} - -#[test] -fn lsp_inlay_hints() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.enable_inlay_hints(); - }); - client.did_open(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 res = client.write_request( - "textDocument/inlayHint", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 19, "character": 0, } - } - }), - ); - assert_eq!( - 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 context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(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 res = client.write_request( - "textDocument/inlayHint", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 19, "character": 0, } - } - }), - ); - assert_eq!(res, json!(null)); -} - -#[test] -fn lsp_workspace_disable_enable_paths() { - fn run_test(use_trailing_slash: bool) { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.create_dir_all("worker"); - temp_dir.write("worker/shared.ts", "export const a = 1"); - temp_dir.write("worker/other.ts", "import { a } from './shared.ts';\na;"); - temp_dir.write("worker/node.ts", "Buffer.alloc(1);"); - - let root_specifier = temp_dir.uri(); - - let mut client = context.new_lsp_command().build(); - client.initialize_with_config( - |builder| { - builder - .set_disable_paths(vec!["./worker/node.ts".to_string()]) - .set_enable_paths(vec!["./worker".to_string()]) - .set_root_uri(root_specifier.clone()) - .set_workspace_folders(vec![lsp::WorkspaceFolder { - uri: if use_trailing_slash { - root_specifier.clone() - } else { - ModuleSpecifier::parse( - root_specifier.as_str().strip_suffix('/').unwrap(), - ) - .unwrap() - }, - name: "project".to_string(), - }]) - .set_deno_enable(false); - }, - json!({ "deno": { - "enable": false, - "disablePaths": ["./worker/node.ts"], - "enablePaths": ["./worker"], - } }), - ); - - client.did_open(json!({ - "textDocument": { - "uri": root_specifier.join("./file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - })); - - client.did_open(json!({ - "textDocument": { - "uri": root_specifier.join("./other/file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - })); - - client.did_open(json!({ - "textDocument": { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": concat!( - "console.log(Date.now());\n", - "import { a } from './shared.ts';\n", - "a;\n", - ), - } - })); - - client.did_open(json!({ - "textDocument": { - "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - })); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": root_specifier.join("./file.ts").unwrap(), - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!(res, json!(null)); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": root_specifier.join("./other/file.ts").unwrap(), - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!(res, json!(null)); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": root_specifier.join("./worker/node.ts").unwrap(), - }, - "position": { "line": 0, "character": 0 } - }), - ); - assert_eq!(res, json!(null)); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!( - res, - 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 res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(), - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!( - res, - 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, } - } - }) - ); - - // check that the file system documents were auto-discovered - // via the enabled paths - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - }, - "position": { "line": 2, "character": 0 }, - "context": { - "includeDeclaration": true - } - }), - ); - - assert_eq!( - res, - json!([{ - "uri": root_specifier.join("./worker/file.ts").unwrap(), - "range": { - "start": { "line": 1, "character": 9 }, - "end": { "line": 1, "character": 10 } - } - }, { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - "range": { - "start": { "line": 2, "character": 0 }, - "end": { "line": 2, "character": 1 } - } - }, { - "uri": root_specifier.join("./worker/shared.ts").unwrap(), - "range": { - "start": { "line": 0, "character": 13 }, - "end": { "line": 0, "character": 14 } - } - }, { - "uri": root_specifier.join("./worker/other.ts").unwrap(), - "range": { - "start": { "line": 0, "character": 9 }, - "end": { "line": 0, "character": 10 } - } - }, { - "uri": root_specifier.join("./worker/other.ts").unwrap(), - "range": { - "start": { "line": 1, "character": 0 }, - "end": { "line": 1, "character": 1 } - } - }]) - ); - - client.shutdown(); - } - - run_test(true); - run_test(false); -} - -#[test] -fn lsp_exclude_config() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.create_dir_all("other"); - temp_dir.write( - "other/shared.ts", - // this should not be found in the "find references" since this file is excluded - "import { a } from '../worker/shared.ts'; console.log(a);", - ); - temp_dir.create_dir_all("worker"); - temp_dir.write("worker/shared.ts", "export const a = 1"); - temp_dir.write( - "deno.json", - r#"{ - "exclude": ["other"], -}"#, - ); - let root_specifier = temp_dir.uri(); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - client.did_open(json!({ - "textDocument": { - "uri": root_specifier.join("./other/file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - })); - - client.did_open(json!({ - "textDocument": { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": concat!( - "console.log(Date.now());\n", - "import { a } from './shared.ts';\n", - "a;\n", - ), - } - })); - - client.did_open(json!({ - "textDocument": { - "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - })); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": root_specifier.join("./other/file.ts").unwrap(), - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!(res, json!(null)); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - }, - "position": { "line": 0, "character": 19 } - }), - ); - assert_eq!( - res, - 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, } - } - }) - ); - - // check that the file system documents were auto-discovered - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - }, - "position": { "line": 2, "character": 0 }, - "context": { - "includeDeclaration": true - } - }), - ); - - assert_eq!( - res, - json!([{ - "uri": root_specifier.join("./worker/file.ts").unwrap(), - "range": { - "start": { "line": 1, "character": 9 }, - "end": { "line": 1, "character": 10 } - } - }, { - "uri": root_specifier.join("./worker/file.ts").unwrap(), - "range": { - "start": { "line": 2, "character": 0 }, - "end": { "line": 2, "character": 1 } - } - }, { - "uri": root_specifier.join("./worker/shared.ts").unwrap(), - "range": { - "start": { "line": 0, "character": 13 }, - "end": { "line": 0, "character": 14 } - } - }]) - ); - - client.shutdown(); -} - -#[test] -fn lsp_hover_unstable_always_enabled() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - // IMPORTANT: If you change this API due to stabilization, also change it - // in the enabled test below. - "text": "type _ = Deno.ForeignLibraryInterface;\n" - } - })); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 14 } - }), - ); - assert_eq!( - res, - json!({ - "contents":[ - { - "language":"typescript", - "value":"interface Deno.ForeignLibraryInterface" - }, - "**UNSTABLE**: New API, yet to be vetted.\n\nA foreign library interface descriptor.", - "\n\n*@category* - FFI", - ], - "range":{ - "start":{ "line":0, "character":14 }, - "end":{ "line":0, "character":37 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_unstable_enabled() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - // NOTE(bartlomieju): this is effectively not used anymore. - builder.set_unstable(true); - }); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "type _ = Deno.ForeignLibraryInterface;\n" - } - })); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 14 } - }), - ); - assert_eq!( - res, - json!({ - "contents":[ - { - "language":"typescript", - "value":"interface Deno.ForeignLibraryInterface" - }, - "**UNSTABLE**: New API, yet to be vetted.\n\nA foreign library interface descriptor.", - "\n\n*@category* - FFI", - ], - "range":{ - "start":{ "line":0, "character":14 }, - "end":{ "line":0, "character":37 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_change_mbc() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 emoji is character index 15 - "character": 15 - } - }, - "text": "" - } - ] - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 2, "character": 15 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value": "const b: \"😃\"", - }, - "", - ], - "range": { - "start": { "line": 2, "character": 15, }, - "end": { "line": 2, "character": 16, }, - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_closed_document() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("a.ts", r#"export const a = "a";"#); - temp_dir.write("b.ts", r#"export * from "./a.ts";"#); - temp_dir.write("c.ts", "import { a } from \"./b.ts\";\nconsole.log(a);\n"); - - let b_specifier = temp_dir.uri().join("b.ts").unwrap(); - let c_specifier = temp_dir.uri().join("c.ts").unwrap(); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": b_specifier, - "languageId": "typescript", - "version": 1, - "text": r#"export * from "./a.ts";"# - } - })); - - client.did_open(json!({ - "textDocument": { - "uri": c_specifier, - "languageId": "typescript", - "version": 1, - "text": "import { a } from \"./b.ts\";\nconsole.log(a);\n", - } - })); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": c_specifier, - }, - "position": { "line": 0, "character": 10 } - }), - ); - assert_eq!( - res, - 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, - } - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": c_specifier, - }, - "position": { "line": 0, "character": 10 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value": "(alias) const a: \"a\"\nimport a" - }, - "" - ], - "range": { - "start": { "line": 0, "character": 9 }, - "end": { "line": 0, "character": 10 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_dependency() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file_01.ts", - "languageId": "typescript", - "version": 1, - "text": "export const a = \"a\";\n", - } - })); - client.did_open( - 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// @deno-types=\"http://127.0.0.1:4545/type_definitions/foo.d.ts\"\nimport * as b from \"http://127.0.0.1:4545/type_definitions/foo.js\";\nimport * as c from \"http://127.0.0.1:4545/subdir/type_reference.js\";\nimport * as d from \"http://127.0.0.1:4545/subdir/mod1.ts\";\nimport * as e from \"data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=\";\nimport * as f from \"./file_01.ts\";\nimport * as g from \"http://localhost:4545/x/a/mod.ts\";\n\nconsole.log(a, b, c, d, e, f, g);\n" - } - }), - ); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [[], "file:///a/file.ts"], - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 0, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http​://127.0.0.1:4545/xTypeScriptTypes.d.ts\n" - }, - "range": { - "start": { "line": 0, "character": 19 }, - "end":{ "line": 0, "character": 62 } - } - }) - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 3, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://127.0.0.1:4545/subdir/type_reference.js\n\n**Types**: http​://127.0.0.1:4545/subdir/type_reference.d.ts\n" - }, - "range": { - "start": { "line": 3, "character": 19 }, - "end":{ "line": 3, "character": 67 } - } - }) - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 4, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://127.0.0.1:4545/subdir/mod1.ts\n" - }, - "range": { - "start": { "line": 4, "character": 19 }, - "end":{ "line": 4, "character": 57 } - } - }) - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 5, "character": 28 } - }), - ); - assert_eq!( - res, - 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 res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 6, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: file​:///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 context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file1.ts", - "languageId": "typescript", - "version": 1, - "text": "export type Foo = { bar(): string };\n" - } - })); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file2.ts", - "languageId": "typescript", - "version": 1, - "text": "import { Foo } from './file1.ts'; declare const f: Foo; f\n" - } - })); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file2.ts" - }, - "position": { "line": 0, "character": 56 } - }), - ); - assert_eq!( - res, - 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": "." - } - ] - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file2.ts" - }, - "position": { "line": 0, "character": 56 } - }), - ); - assert_eq!( - res, - json!({ - "contents": [ - { - "language": "typescript", - "value": "const f: Foo", - }, - "" - ], - "range": { - "start": { "line": 0, "character": 56, }, - "end": { "line": 0, "character": 57, } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_typescript_types() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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", - } - }), - ); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - ["http://127.0.0.1:4545/xTypeScriptTypes.js"], - "file:///a/file.ts", - ], - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 24 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http​://127.0.0.1:4545/xTypeScriptTypes.d.ts\n" - }, - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 62 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_hover_jsdoc_symbol_link() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/b.ts", - "languageId": "typescript", - "version": 1, - "text": "export function hello() {}\n" - } - })); - client.did_open( - 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 res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 7, "character": 10 } - }), - ); - assert_eq!( - res, - 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 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_goto_type_definition() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/typeDefinition", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 12, "character": 1 } - }), - ); - assert_eq!( - res, - 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 } - } - } - ]) - ); - client.shutdown(); -} - -#[test] -fn lsp_call_hierarchy() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/prepareCallHierarchy", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 5, "character": 3 } - }), - ); - assert_eq!( - res, - json!([{ - "name": "baz", - "kind": 6, - "detail": "Bar", - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 7, "character": 3 } - }, - "selectionRange": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 5, "character": 5 } - } - }]) - ); - let res = client.write_request( - "callHierarchy/incomingCalls", - json!({ - "item": { - "name": "baz", - "kind": 6, - "detail": "Bar", - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 7, "character": 3 } - }, - "selectionRange": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 5, "character": 5 } - } - } - }), - ); - assert_eq!( - res, - json!([{ - "from": { - "name": "main", - "kind": 12, - "detail": "", - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 10, "character": 0 }, - "end": { "line": 13, "character": 1 } - }, - "selectionRange": { - "start": { "line": 10, "character": 9 }, - "end": { "line": 10, "character": 13 } - } - }, - "fromRanges": [ - { - "start": { "line": 12, "character": 6 }, - "end": { "line": 12, "character": 9 } - } - ] - }]) - ); - let res = client.write_request( - "callHierarchy/outgoingCalls", - json!({ - "item": { - "name": "baz", - "kind": 6, - "detail": "Bar", - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 7, "character": 3 } - }, - "selectionRange": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 5, "character": 5 } - } - } - }), - ); - assert_eq!( - res, - json!([{ - "to": { - "name": "foo", - "kind": 12, - "detail": "", - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 2, "character": 1 } - }, - "selectionRange": { - "start": { "line": 0, "character": 9 }, - "end": { "line": 0, "character": 12 } - } - }, - "fromRanges": [{ - "start": { "line": 6, "character": 11 }, - "end": { "line": 6, "character": 14 } - }] - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_large_doc_changes() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let large_file_text = - fs::read_to_string(testdata_path().join("lsp").join("large_file.txt")) - .unwrap(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "javascript", - "version": 1, - "text": large_file_text, - } - })); - 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": "+++" - } - ] - }), - ); - 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": "// " - } - ] - }), - ); - 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" - } - ] - }), - ); - client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 421, "character": 30 } - }), - ); - client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 444, "character": 6 } - }), - ); - client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 461, "character": 34 } - }), - ); - client.shutdown(); - - assert!(client.duration().as_millis() <= 15000); -} - -#[test] -fn lsp_document_symbol() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "interface IFoo {\n foo(): boolean;\n}\n\nclass Bar implements IFoo {\n constructor(public x: number) { }\n foo() { return true; }\n /** @deprecated */\n baz() { return false; }\n get value(): number { return 0; }\n set value(_newValue: number) { return; }\n static staticBar = new Bar(0);\n private static getStaticBar() { return Bar.staticBar; }\n}\n\nenum Values { value1, value2 }\n\nvar bar: IFoo = new Bar(3);" - } - }), - ); - let res = client.write_request( - "textDocument/documentSymbol", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - json!([{ - "name": "bar", - "kind": 13, - "range": { - "start": { "line": 17, "character": 4 }, - "end": { "line": 17, "character": 26 } - }, - "selectionRange": { - "start": { "line": 17, "character": 4 }, - "end": { "line": 17, "character": 7 } - } - }, { - "name": "Bar", - "kind": 5, - "range": { - "start": { "line": 4, "character": 0 }, - "end": { "line": 13, "character": 1 } - }, - "selectionRange": { - "start": { "line": 4, "character": 6 }, - "end": { "line": 4, "character": 9 } - }, - "children": [{ - "name": "constructor", - "kind": 9, - "range": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 5, "character": 35 } - }, - "selectionRange": { - "start": { "line": 5, "character": 2 }, - "end": { "line": 5, "character": 35 } - } - }, { - "name": "baz", - "kind": 6, - "tags": [1], - "range": { - "start": { "line": 8, "character": 2 }, - "end": { "line": 8, "character": 25 } - }, - "selectionRange": { - "start": { "line": 8, "character": 2 }, - "end": { "line": 8, "character": 5 } - } - }, { - "name": "foo", - "kind": 6, - "range": { - "start": { "line": 6, "character": 2 }, - "end": { "line": 6, "character": 24 } - }, - "selectionRange": { - "start": { "line": 6, "character": 2 }, - "end": { "line": 6, "character": 5 } - } - }, { - "name": "getStaticBar", - "kind": 6, - "range": { - "start": { "line": 12, "character": 2 }, - "end": { "line": 12, "character": 57 } - }, - "selectionRange": { - "start": { "line": 12, "character": 17 }, - "end": { "line": 12, "character": 29 } - } - }, { - "name": "staticBar", - "kind": 8, - "range": { - "start": { "line": 11, "character": 2 }, - "end": { "line": 11, "character": 32 } - }, - "selectionRange": { - "start": { "line": 11, "character": 9 }, - "end": { "line": 11, "character": 18 } - } - }, { - "name": "(get) value", - "kind": 8, - "range": { - "start": { "line": 9, "character": 2 }, - "end": { "line": 9, "character": 35 } - }, - "selectionRange": { - "start": { "line": 9, "character": 6 }, - "end": { "line": 9, "character": 11 } - } - }, { - "name": "(set) value", - "kind": 8, - "range": { - "start": { "line": 10, "character": 2 }, - "end": { "line": 10, "character": 42 } - }, - "selectionRange": { - "start": { "line": 10, "character": 6 }, - "end": { "line": 10, "character": 11 } - } - }, { - "name": "x", - "kind": 8, - "range": { - "start": { "line": 5, "character": 14 }, - "end": { "line": 5, "character": 30 } - }, - "selectionRange": { - "start": { "line": 5, "character": 21 }, - "end": { "line": 5, "character": 22 } - } - }] - }, { - "name": "IFoo", - "kind": 11, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 2, "character": 1 } - }, - "selectionRange": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 14 } - }, - "children": [{ - "name": "foo", - "kind": 6, - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 17 } - }, - "selectionRange": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 5 } - } - }] - }, { - "name": "Values", - "kind": 10, - "range": { - "start": { "line": 15, "character": 0 }, - "end": { "line": 15, "character": 30 } - }, - "selectionRange": { - "start": { "line": 15, "character": 5 }, - "end": { "line": 15, "character": 11 } - }, - "children": [{ - "name": "value1", - "kind": 22, - "range": { - "start": { "line": 15, "character": 14 }, - "end": { "line": 15, "character": 20 } - }, - "selectionRange": { - "start": { "line": 15, "character": 14 }, - "end": { "line": 15, "character": 20 } - } - }, { - "name": "value2", - "kind": 22, - "range": { - "start": { "line": 15, "character": 22 }, - "end": { "line": 15, "character": 28 } - }, - "selectionRange": { - "start": { "line": 15, "character": 22 }, - "end": { "line": 15, "character": 28 } - } - }] - }] - ) - ); - client.shutdown(); -} - -#[test] -fn lsp_folding_range() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/foldingRange", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - 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 - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_rename() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/rename", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 4 }, - "newName": "variable_modified" - }), - ); - assert_eq!( - res, - json!({ - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 4 }, - "end": { "line": 0, "character": 12 } - }, - "newText": "variable_modified" - }, { - "range": { - "start": { "line": 1, "character": 12 }, - "end": { "line": 1, "character": 20 } - }, - "newText": "variable_modified" - }] - }] - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_selection_range() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/selectionRange", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "positions": [{ "line": 2, "character": 8 }] - }), - ); - assert_eq!( - res, - json!([{ - "range": { - "start": { "line": 2, "character": 8 }, - "end": { "line": 2, "character": 9 } - }, - "parent": { - "range": { - "start": { "line": 2, "character": 8 }, - "end": { "line": 2, "character": 15 } - }, - "parent": { - "range": { - "start": { "line": 2, "character": 4 }, - "end": { "line": 4, "character": 5 } - }, - "parent": { - "range": { - "start": { "line": 1, "character": 13 }, - "end": { "line": 6, "character": 2 } - }, - "parent": { - "range": { - "start": { "line": 1, "character": 12 }, - "end": { "line": 6, "character": 3 } - }, - "parent": { - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 6, "character": 3 } - }, - "parent": { - "range": { - "start": { "line": 0, "character": 11 }, - "end": { "line": 7, "character": 0 } - }, - "parent": { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 7, "character": 1 } - } - } - } - } - } - } - } - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_semantic_tokens() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "enum Values { value1, value2 }\n\nasync function baz(s: string): Promise<string> {\n const r = s.slice(0);\n return r;\n}\n\ninterface IFoo {\n readonly x: number;\n foo(): boolean;\n}\n\nclass Bar implements IFoo {\n constructor(public readonly x: number) { }\n foo() { return true; }\n static staticBar = new Bar(0);\n private static getStaticBar() { return Bar.staticBar; }\n}\n" - } - }), - ); - let res = client.write_request( - "textDocument/semanticTokens/full", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - 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 res = client.write_request( - "textDocument/semanticTokens/range", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 6, "character": 0 } - } - }), - ); - assert_eq!( - res, - 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 - ] - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_lens() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": concat!( - "class A {\n", - " a = \"a\";\n", - "\n", - " b() {\n", - " console.log(this.a);\n", - " }\n", - "\n", - " c() {\n", - " this.a = \"c\";\n", - " }\n", - "}\n", - "\n", - "const a = new A();\n", - "a.b();\n", - "const b = 2;\n", - "const c = 3;\n", - "c; c;", - ), - } - })); - let res = client.write_request( - "textDocument/codeLens", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - json!([{ - "range": { - "start": { "line": 0, "character": 6 }, - "end": { "line": 0, "character": 7 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 3 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }]) - ); - let res = 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" - } - }), - ); - assert_eq!( - res, - json!({ - "range": { - "start": { "line": 0, "character": 6 }, - "end": { "line": 0, "character": 7 } - }, - "command": { - "title": "1 reference", - "command": "deno.client.showReferences", - "arguments": [ - "file:///a/file.ts", - { "line": 0, "character": 6 }, - [{ - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 12, "character": 14 }, - "end": { "line": 12, "character": 15 } - } - }] - ] - } - }) - ); - - // 0 references - let res = client.write_request( - "codeLens/resolve", - json!({ - "range": { - "start": { "line": 14, "character": 6 }, - "end": { "line": 14, "character": 7 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }), - ); - assert_eq!( - res, - json!({ - "range": { - "start": { "line": 14, "character": 6 }, - "end": { "line": 14, "character": 7 } - }, - "command": { - "title": "0 references", - "command": "", - } - }) - ); - - // 2 references - let res = client.write_request( - "codeLens/resolve", - json!({ - "range": { - "start": { "line": 15, "character": 6 }, - "end": { "line": 15, "character": 7 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }), - ); - assert_eq!( - res, - json!({ - "range": { - "start": { "line": 15, "character": 6 }, - "end": { "line": 15, "character": 7 } - }, - "command": { - "title": "2 references", - "command": "deno.client.showReferences", - "arguments": [ - "file:///a/file.ts", - { "line": 15, "character": 6 }, - [{ - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 16, "character": 0 }, - "end": { "line": 16, "character": 1 } - } - },{ - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 16, "character": 3 }, - "end": { "line": 16, "character": 4 } - } - }] - ] - } - }) - ); - - client.shutdown(); -} - -#[test] -fn lsp_code_lens_impl() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/codeLens", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - json!([ { - "range": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations" - } - }, { - "range": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 4, "character": 6 }, - "end": { "line": 4, "character": 7 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 10, "character": 10 }, - "end": { "line": 10, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations" - } - }, { - "range": { - "start": { "line": 10, "character": 10 }, - "end": { "line": 10, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 11, "character": 2 }, - "end": { "line": 11, "character": 3 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }]) - ); - let res = 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" - } - }), - ); - assert_eq!( - res, - json!({ - "range": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 11 } - }, - "command": { - "title": "1 implementation", - "command": "deno.client.showReferences", - "arguments": [ - "file:///a/file.ts", - { "line": 0, "character": 10 }, - [{ - "uri": "file:///a/file.ts", - "range": { - "start": { "line": 4, "character": 6 }, - "end": { "line": 4, "character": 7 } - } - }] - ] - } - }) - ); - let res = client.write_request( - "codeLens/resolve", - json!({ - "range": { - "start": { "line": 10, "character": 10 }, - "end": { "line": 10, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations" - } - }), - ); - assert_eq!( - res, - json!({ - "range": { - "start": { "line": 10, "character": 10 }, - "end": { "line": 10, "character": 11 } - }, - "command": { - "title": "0 implementations", - "command": "" - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_lens_test() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.disable_testing_api().set_code_lens(None); - }); - client.did_open( - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "const { test } = Deno;\nconst { test: test2 } = Deno;\nconst test3 = Deno.test;\n\nDeno.test(\"test a\", () => {});\nDeno.test({\n name: \"test b\",\n fn() {},\n});\ntest({\n name: \"test c\",\n fn() {},\n});\ntest(\"test d\", () => {});\ntest2({\n name: \"test e\",\n fn() {},\n});\ntest2(\"test f\", () => {});\ntest3({\n name: \"test g\",\n fn() {},\n});\ntest3(\"test h\", () => {});\n" - } - }), - ); - let res = client.write_request( - "textDocument/codeLens", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - json!([{ - "range": { - "start": { "line": 4, "character": 5 }, - "end": { "line": 4, "character": 9 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test a", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 4, "character": 5 }, - "end": { "line": 4, "character": 9 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test a", - { "inspect": true } - ] - } - }, { - "range": { - "start": { "line": 5, "character": 5 }, - "end": { "line": 5, "character": 9 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test b", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 5, "character": 5 }, - "end": { "line": 5, "character": 9 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test b", - { "inspect": true } - ] - } - }, { - "range": { - "start": { "line": 9, "character": 0 }, - "end": { "line": 9, "character": 4 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test c", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 9, "character": 0 }, - "end": { "line": 9, "character": 4 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test c", - { "inspect": true } - ] - } - }, { - "range": { - "start": { "line": 13, "character": 0 }, - "end": { "line": 13, "character": 4 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test d", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 13, "character": 0 }, - "end": { "line": 13, "character": 4 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test d", - { "inspect": true } - ] - } - }, { - "range": { - "start": { "line": 14, "character": 0 }, - "end": { "line": 14, "character": 5 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test e", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 14, "character": 0 }, - "end": { "line": 14, "character": 5 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test e", - { "inspect": true } - ] - } - }, { - "range": { - "start": { "line": 18, "character": 0 }, - "end": { "line": 18, "character": 5 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test f", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 18, "character": 0 }, - "end": { "line": 18, "character": 5 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test f", - { "inspect": true } - ] - } - }, { - "range": { - "start": { "line": 19, "character": 0 }, - "end": { "line": 19, "character": 5 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test g", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 19, "character": 0 }, - "end": { "line": 19, "character": 5 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test g", - { "inspect": true } - ] - } - }, { - "range": { - "start": { "line": 23, "character": 0 }, - "end": { "line": 23, "character": 5 } - }, - "command": { - "title": "▶︎ Run Test", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test h", - { "inspect": false } - ] - } - }, { - "range": { - "start": { "line": 23, "character": 0 }, - "end": { "line": 23, "character": 5 } - }, - "command": { - "title": "Debug", - "command": "deno.client.test", - "arguments": [ - "file:///a/file.ts", - "test h", - { "inspect": true } - ] - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_lens_test_disabled() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.disable_testing_api().set_code_lens(Some(json!({ - "implementations": true, - "references": true, - "test": false - }))); - }); - client.change_configuration(json!({ - "deno": { - "enable": true, - "codeLens": { - "test": false, - }, - }, - })); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "const { test } = Deno;\nconst { test: test2 } = Deno;\nconst test3 = Deno.test;\n\nDeno.test(\"test a\", () => {});\nDeno.test({\n name: \"test b\",\n fn() {},\n});\ntest({\n name: \"test c\",\n fn() {},\n});\ntest(\"test d\", () => {});\ntest2({\n name: \"test e\",\n fn() {},\n});\ntest2(\"test f\", () => {});\ntest3({\n name: \"test g\",\n fn() {},\n});\ntest3(\"test h\", () => {});\n" - }, - })); - let res = client.write_request( - "textDocument/codeLens", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!(res, json!(null)); - client.shutdown(); -} - -#[test] -fn lsp_code_lens_non_doc_nav_tree() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - })); - client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 3 }, - "context": { - "includeDeclaration": true - } - }), - ); - client.write_request( - "deno/virtualTextDocument", - json!({ - "textDocument": { - "uri": "deno:/asset/lib.deno.shared_globals.d.ts" - } - }), - ); - let res = client.write_request_with_res_as::<Vec<lsp::CodeLens>>( - "textDocument/codeLens", - json!({ - "textDocument": { - "uri": "deno:/asset/lib.deno.shared_globals.d.ts" - } - }), - ); - assert!(res.len() > 50); - client.write_request_with_res_as::<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" - } - }), - ); - client.shutdown(); -} - -#[test] -fn lsp_nav_tree_updates() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/codeLens", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - json!([ { - "range": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations" - } - }, { - "range": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 4, "character": 6 }, - "end": { "line": 4, "character": 7 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 10, "character": 10 }, - "end": { "line": 10, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations" - } - }, { - "range": { - "start": { "line": 10, "character": 10 }, - "end": { "line": 10, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 11, "character": 2 }, - "end": { "line": 11, "character": 3 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }]) - ); - 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": "" - } - ] - }), - ); - let res = client.write_request( - "textDocument/codeLens", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - json!([{ - "range": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations" - } - }, { - "range": { - "start": { "line": 0, "character": 10 }, - "end": { "line": 0, "character": 11 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }, { - "range": { - "start": { "line": 4, "character": 6 }, - "end": { "line": 4, "character": 7 } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_find_references() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/mod.ts", - "languageId": "typescript", - "version": 1, - "text": r"export const a = 1;\nconst b = 2;" - } - })); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/mod.test.ts", - "languageId": "typescript", - "version": 1, - "text": r#"import { a } from './mod.ts'; console.log(a);"# - } - })); - - // test without including the declaration - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": "file:///a/mod.ts", - }, - "position": { "line": 0, "character": 13 }, - "context": { - "includeDeclaration": false - } - }), - ); - - assert_eq!( - res, - json!([{ - "uri": "file:///a/mod.test.ts", - "range": { - "start": { "line": 0, "character": 9 }, - "end": { "line": 0, "character": 10 } - } - }, { - "uri": "file:///a/mod.test.ts", - "range": { - "start": { "line": 0, "character": 42 }, - "end": { "line": 0, "character": 43 } - } - }]) - ); - - // test with including the declaration - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": "file:///a/mod.ts", - }, - "position": { "line": 0, "character": 13 }, - "context": { - "includeDeclaration": true - } - }), - ); - - assert_eq!( - res, - json!([{ - "uri": "file:///a/mod.ts", - "range": { - "start": { "line": 0, "character": 13 }, - "end": { "line": 0, "character": 14 } - } - }, { - "uri": "file:///a/mod.test.ts", - "range": { - "start": { "line": 0, "character": 9 }, - "end": { "line": 0, "character": 10 } - } - }, { - "uri": "file:///a/mod.test.ts", - "range": { - "start": { "line": 0, "character": 42 }, - "end": { "line": 0, "character": 43 } - } - }]) - ); - - // test 0 references - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": "file:///a/mod.ts", - }, - "position": { "line": 1, "character": 6 }, - "context": { - "includeDeclaration": false - } - }), - ); - - assert_eq!(res, json!(null)); // seems it always returns null for this, which is ok - - client.shutdown(); -} - -#[test] -fn lsp_signature_help() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.write_request( - "textDocument/signatureHelp", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "character": 4, "line": 9 }, - "context": { - "triggerKind": 2, - "triggerCharacter": "(", - "isRetrigger": false - } - }), - ); - assert_eq!( - res, - 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, " - } - ] - }), - ); - let res = client.write_request( - "textDocument/signatureHelp", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "character": 8, "line": 9 } - }), - ); - assert_eq!( - res, - 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 - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_actions() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client - .write_request( "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 7 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 7 } - }, - "severity": 1, - "code": 1308, - "source": "deno-ts", - "message": "'await' expressions are only allowed within async functions and at the top levels of modules.", - "relatedInformation": [] - }], - "only": ["quickfix"] - } - }), - ) - ; - assert_eq!( - res, - json!([{ - "title": "Add async modifier to containing function", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 7 } - }, - "severity": 1, - "code": 1308, - "source": "deno-ts", - "message": "'await' expressions are only allowed within async functions and at the top levels of modules.", - "relatedInformation": [] - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 7 }, - "end": { "line": 0, "character": 7 } - }, - "newText": "async " - }, { - "range": { - "start": { "line": 0, "character": 21 }, - "end": { "line": 0, "character": 25 } - }, - "newText": "Promise<void>" - }] - }] - } - }, { - "title": "Add all missing 'async' modifiers", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 7 } - }, - "severity": 1, - "code": 1308, - "source": "deno-ts", - "message": "'await' expressions are only allowed within async functions and at the top levels of modules.", - "relatedInformation": [] - }], - "data": { - "specifier": "file:///a/file.ts", - "fixId": "fixAwaitInSyncFunction" - } - }]) - ); - let res = client - .write_request( "codeAction/resolve", - json!({ - "title": "Add all missing 'async' modifiers", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 2 }, - "end": { "line": 1, "character": 7 } - }, - "severity": 1, - "code": 1308, - "source": "deno-ts", - "message": "'await' expressions are only allowed within async functions and at the top levels of modules.", - "relatedInformation": [] - }], - "data": { - "specifier": "file:///a/file.ts", - "fixId": "fixAwaitInSyncFunction" - } - }), - ) - ; - assert_eq!( - res, - json!({ - "title": "Add all missing 'async' modifiers", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { - "line": 1, - "character": 2 - }, - "end": { - "line": 1, - "character": 7 - } - }, - "severity": 1, - "code": 1308, - "source": "deno-ts", - "message": "'await' expressions are only allowed within async functions and at the top levels of modules.", - "relatedInformation": [] - } - ], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 7 }, - "end": { "line": 0, "character": 7 } - }, - "newText": "async " - }, { - "range": { - "start": { "line": 0, "character": 21 }, - "end": { "line": 0, "character": 25 } - }, - "newText": "Promise<void>" - }, { - "range": { - "start": { "line": 4, "character": 7 }, - "end": { "line": 4, "character": 7 } - }, - "newText": "async " - }, { - "range": { - "start": { "line": 4, "character": 21 }, - "end": { "line": 4, "character": 25 } - }, - "newText": "Promise<void>" - }] - }] - }, - "data": { - "specifier": "file:///a/file.ts", - "fixId": "fixAwaitInSyncFunction" - } - }) - ); - client.shutdown(); -} - -#[test] -fn test_lsp_code_actions_ordering() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": r#" - import "https://deno.land/x/a/mod.ts"; - let a = "a"; - console.log(a); - export function b(): void { - await Promise.resolve("b"); - } - "# - } - })); - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 1, "character": 11 }, - "end": { "line": 6, "character": 12 } - }, - "context": { - "diagnostics": diagnostics.all(), - "only": ["quickfix"] - } - }), - ); - - // Simplify the serialization to `{ title, source }` for this test. - let mut actions: Vec<Value> = serde_json::from_value(res).unwrap(); - for action in &mut actions { - let action = action.as_object_mut().unwrap(); - let title = action.get("title").unwrap().as_str().unwrap().to_string(); - let diagnostics = action.get("diagnostics").unwrap().as_array().unwrap(); - let diagnostic = diagnostics.first().unwrap().as_object().unwrap(); - let source = diagnostic.get("source").unwrap(); - let source = source.as_str().unwrap().to_string(); - action.clear(); - action.insert("title".to_string(), serde_json::to_value(title).unwrap()); - action.insert("source".to_string(), serde_json::to_value(source).unwrap()); - } - let res = serde_json::to_value(actions).unwrap(); - - // Ensure ordering is "deno-ts" -> "deno" -> "deno-lint". - assert_eq!( - res, - json!([ - { - "title": "Add async modifier to containing function", - "source": "deno-ts", - }, - { - "title": "Cache \"https://deno.land/x/a/mod.ts\" and its dependencies.", - "source": "deno", - }, - { - "title": "Disable prefer-const for this line", - "source": "deno-lint", - }, - { - "title": "Disable prefer-const for the entire file", - "source": "deno-lint", - }, - { - "title": "Ignore lint errors for the entire file", - "source": "deno-lint", - }, - { - "title": "Disable no-await-in-sync-fn for this line", - "source": "deno-lint", - }, - { - "title": "Disable no-await-in-sync-fn for the entire file", - "source": "deno-lint", - }, - { - "title": "Ignore lint errors for the entire file", - "source": "deno-lint", - }, - ]) - ); -} - -#[test] -fn lsp_status_file() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - let res = client.write_request( - "deno/virtualTextDocument", - json!({ - "textDocument": { - "uri": "deno:/status.md" - } - }), - ); - let res = res.as_str().unwrap().to_string(); - assert!(res.starts_with("# Deno Language Server Status")); - - let res = client.write_request( - "deno/virtualTextDocument", - json!({ - "textDocument": { - "uri": "deno:/status.md?1" - } - }), - ); - let res = res.as_str().unwrap().to_string(); - assert!(res.starts_with("# Deno Language Server Status")); -} - -#[test] -fn lsp_code_actions_deno_cache() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.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.messages_with_source("deno"), - serde_json::from_value(json!({ - "uri": "file:///a/file.ts", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 49 } - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Uncached or missing remote URL: https://deno.land/x/a/mod.ts", - "data": { "specifier": "https://deno.land/x/a/mod.ts" } - }], - "version": 1 - })).unwrap() - ); - - let res = - client - .write_request( "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 49 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 49 } - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Unable to load the remote module: \"https://deno.land/x/a/mod.ts\".", - "data": { - "specifier": "https://deno.land/x/a/mod.ts" - } - }], - "only": ["quickfix"] - } - }), - ) - ; - assert_eq!( - res, - json!([{ - "title": "Cache \"https://deno.land/x/a/mod.ts\" and its dependencies.", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 49 } - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Unable to load the remote module: \"https://deno.land/x/a/mod.ts\".", - "data": { - "specifier": "https://deno.land/x/a/mod.ts" - } - }], - "command": { - "title": "", - "command": "deno.cache", - "arguments": [["https://deno.land/x/a/mod.ts"], "file:///a/file.ts"] - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_jsr_uncached() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": r#"import "jsr:@foo/bar";"#, - }, - })); - // TODO(nayeemrmn): This should check if the jsr dep is cached and give a - // diagnostic. - assert_eq!(json!(diagnostics.all()), json!([])); - client.shutdown(); -} - -#[test] -fn lsp_code_actions_deno_cache_npm() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.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.messages_with_source("deno"), - serde_json::from_value(json!({ - "uri": "file:///a/file.ts", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 18 }, - "end": { "line": 0, "character": 29 } - }, - "severity": 1, - "code": "no-cache-npm", - "source": "deno", - "message": "Uncached or missing npm package: chalk", - "data": { "specifier": "npm:chalk" } - }], - "version": 1 - })) - .unwrap() - ); - - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 0, "character": 18 }, - "end": { "line": 0, "character": 29 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 18 }, - "end": { "line": 0, "character": 29 } - }, - "severity": 1, - "code": "no-cache-npm", - "source": "deno", - "message": "Uncached or missing npm package: chalk", - "data": { "specifier": "npm:chalk" } - }], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Cache \"npm:chalk\" and its dependencies.", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 18 }, - "end": { "line": 0, "character": 29 } - }, - "severity": 1, - "code": "no-cache-npm", - "source": "deno", - "message": "Uncached or missing npm package: chalk", - "data": { "specifier": "npm:chalk" } - }], - "command": { - "title": "", - "command": "deno.cache", - "arguments": [["npm:chalk"], "file:///a/file.ts"] - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_actions_deno_cache_all() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": r#" - import * as a from "https://deno.land/x/a/mod.ts"; - import chalk from "npm:chalk"; - console.log(a); - console.log(chalk); - "#, - } - })); - assert_eq!( - diagnostics.messages_with_source("deno"), - serde_json::from_value(json!({ - "uri": "file:///a/file.ts", - "diagnostics": [ - { - "range": { - "start": { "line": 1, "character": 27 }, - "end": { "line": 1, "character": 57 }, - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Uncached or missing remote URL: https://deno.land/x/a/mod.ts", - "data": { "specifier": "https://deno.land/x/a/mod.ts" }, - }, - { - "range": { - "start": { "line": 2, "character": 26 }, - "end": { "line": 2, "character": 37 }, - }, - "severity": 1, - "code": "no-cache-npm", - "source": "deno", - "message": "Uncached or missing npm package: chalk", - "data": { "specifier": "npm:chalk" }, - }, - ], - "version": 1, - })).unwrap() - ); - - let res = - client - .write_request( "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "range": { - "start": { "line": 1, "character": 27 }, - "end": { "line": 1, "character": 57 }, - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 27 }, - "end": { "line": 1, "character": 57 }, - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Uncached or missing remote URL: https://deno.land/x/a/mod.ts", - "data": { - "specifier": "https://deno.land/x/a/mod.ts", - }, - }], - "only": ["quickfix"], - } - }), - ) - ; - assert_eq!( - res, - json!([ - { - "title": "Cache \"https://deno.land/x/a/mod.ts\" and its dependencies.", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 27 }, - "end": { "line": 1, "character": 57 }, - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Uncached or missing remote URL: https://deno.land/x/a/mod.ts", - "data": { - "specifier": "https://deno.land/x/a/mod.ts", - }, - }], - "command": { - "title": "", - "command": "deno.cache", - "arguments": [["https://deno.land/x/a/mod.ts"], "file:///a/file.ts"], - } - }, - { - "title": "Cache all dependencies of this module.", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 1, "character": 27 }, - "end": { "line": 1, "character": 57 }, - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Uncached or missing remote URL: https://deno.land/x/a/mod.ts", - "data": { - "specifier": "https://deno.land/x/a/mod.ts", - }, - }, - { - "range": { - "start": { "line": 2, "character": 26 }, - "end": { "line": 2, "character": 37 }, - }, - "severity": 1, - "code": "no-cache-npm", - "source": "deno", - "message": "Uncached or missing npm package: chalk", - "data": { "specifier": "npm:chalk" }, - }, - ], - "command": { - "title": "", - "command": "deno.cache", - "arguments": [[], "file:///a/file.ts"], - } - }, - ]) - ); - client.shutdown(); -} - -#[test] -fn lsp_cache_on_save() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "file.ts", - r#" - import { printHello } from "http://localhost:4545/subdir/print_hello.ts"; - printHello(); - "#, - ); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.change_configuration(json!({ - "deno": { - "enable": true, - "cacheOnSave": true, - }, - })); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("file.ts"), - } - })); - assert_eq!( - diagnostics.messages_with_source("deno"), - serde_json::from_value(json!({ - "uri": temp_dir.uri().join("file.ts").unwrap(), - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 33 }, - "end": { "line": 1, "character": 78 } - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Uncached or missing remote URL: http://localhost:4545/subdir/print_hello.ts", - "data": { "specifier": "http://localhost:4545/subdir/print_hello.ts" } - }], - "version": 1 - })) - .unwrap() - ); - client.did_save(json!({ - "textDocument": { "uri": temp_dir.uri().join("file.ts").unwrap() }, - })); - assert_eq!(client.read_diagnostics().all(), vec![]); - - client.shutdown(); -} - -// Regression test for https://github.com/denoland/deno/issues/22122. -#[test] -fn lsp_cache_then_definition() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": r#"import "http://localhost:4545/run/002_hello.ts";"#, - }, - })); - // Prior to the fix, this would cause a faulty memoization that maps the - // URL "http://localhost:4545/run/002_hello.ts" to itself, preventing it from - // being reverse-mapped to "deno:/http/localhost%3A4545/run/002_hello.ts" on - // "textDocument/definition" request. - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - ["http://localhost:4545/run/002_hello.ts"], - temp_dir.uri().join("file.ts").unwrap(), - ], - }), - ); - let res = client.write_request( - "textDocument/definition", - json!({ - "textDocument": { "uri": temp_dir.uri().join("file.ts").unwrap() }, - "position": { "line": 0, "character": 8 }, - }), - ); - assert_eq!( - res, - json!([{ - "targetUri": "deno:/http/localhost%3A4545/run/002_hello.ts", - "targetRange": { - "start": { - "line": 0, - "character": 0, - }, - "end": { - "line": 1, - "character": 0, - }, - }, - "targetSelectionRange": { - "start": { - "line": 0, - "character": 0, - }, - "end": { - "line": 1, - "character": 0, - }, - }, - }]), - ); -} - -#[test] -fn lsp_code_actions_imports() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.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); - } -} -"# - } - })); - client.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; - } -} -"# - } - })); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file02.ts", - "languageId": "typescript", - "version": 1, - "text": r#"export interface DuckConfigOptions { - kind: string; - quacks: boolean; -} -"# - } - })); - - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file00.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 6, "character": 0 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 50 }, - "end": { "line": 0, "character": 67 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }, { - "range": { - "start": { "line": 4, "character": 39 }, - "end": { "line": 4, "character": 49 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfig'." - }], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Add import from \"./file02.ts\"", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 50 }, - "end": { "line": 0, "character": 67 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file00.ts", - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { DuckConfigOptions } from \"./file02.ts\";\n\n" - }] - }] - } - }, { - "title": "Add import from \"./file01.ts\"", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 4, "character": 39 }, - "end": { "line": 4, "character": 49 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfig'." - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file00.ts", - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { DuckConfig } from \"./file01.ts\";\n\n" - }] - }] - } - }, { - "title": "Add all missing imports", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 50 }, - "end": { "line": 0, "character": 67 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }], - "data": { - "specifier": "file:///a/file00.ts", - "fixId": "fixMissingImport" - } - }]) - ); - let res = client.write_request( - "codeAction/resolve", - json!({ - "title": "Add all missing imports", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 50 }, - "end": { "line": 0, "character": 67 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }, { - "range": { - "start": { "line": 4, "character": 39 }, - "end": { "line": 4, "character": 49 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfig'." - }], - "data": { - "specifier": "file:///a/file00.ts", - "fixId": "fixMissingImport" - } - }), - ); - assert_eq!( - res, - json!({ - "title": "Add all missing imports", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 50 }, - "end": { "line": 0, "character": 67 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }, { - "range": { - "start": { "line": 4, "character": 39 }, - "end": { "line": 4, "character": 49 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfig'." - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file00.ts", - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { DuckConfig } from \"./file01.ts\";\nimport { DuckConfigOptions } from \"./file02.ts\";\n\n" - }] - }] - }, - "data": { - "specifier": "file:///a/file00.ts", - "fixId": "fixMissingImport" - } - }) - ); - - client.shutdown(); -} - -#[test] -fn lsp_code_actions_refactor() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "var x: { a?: number; b?: string } = {};\n" - } - })); - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "context": { - "diagnostics": [], - "only": ["refactor"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Move to a new file", - "kind": "refactor.move.newFile", - "isPreferred": false, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Move to a new file", - "actionName": "Move to a new file" - } - }, { - "title": "Extract to function in module scope", - "kind": "refactor.extract.function", - "isPreferred": false, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Extract Symbol", - "actionName": "function_scope_0" - } - }, { - "title": "Extract to constant in enclosing scope", - "kind": "refactor.extract.constant", - "isPreferred": false, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Extract Symbol", - "actionName": "constant_scope_0" - } - }, { - "title": "Convert default export to named export", - "kind": "refactor.rewrite.export.named", - "isPreferred": false, - "disabled": { - "reason": "This file already has a default export" - }, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Convert export", - "actionName": "Convert default export to named export" - } - }, { - "title": "Convert named export to default export", - "kind": "refactor.rewrite.export.default", - "isPreferred": false, - "disabled": { - "reason": "This file already has a default export" - }, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Convert export", - "actionName": "Convert named export to default export" - } - }, { - "title": "Convert namespace import to named imports", - "kind": "refactor.rewrite.import.named", - "isPreferred": false, - "disabled": { - "reason": "Selection is not an import declaration." - }, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Convert import", - "actionName": "Convert namespace import to named imports" - } - }, { - "title": "Convert named imports to default import", - "kind": "refactor.rewrite.import.default", - "isPreferred": false, - "disabled": { - "reason": "Selection is not an import declaration." - }, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Convert import", - "actionName": "Convert named imports to default import" - } - }, { - "title": "Convert named imports to namespace import", - "kind": "refactor.rewrite.import.namespace", - "isPreferred": false, - "disabled": { - "reason": "Selection is not an import declaration." - }, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "refactorName": "Convert import", - "actionName": "Convert named imports to namespace import" - } - }]) - ); - let res = client.write_request( - "codeAction/resolve", - json!({ - "title": "Extract to interface", - "kind": "refactor.extract.interface", - "isPreferred": true, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 7 }, - "end": { "line": 0, "character": 33 } - }, - "refactorName": "Extract type", - "actionName": "Extract to interface" - } - }), - ); - assert_eq!( - res, - json!({ - "title": "Extract to interface", - "kind": "refactor.extract.interface", - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "interface NewType {\n a?: number;\n b?: string;\n}\n\n" - }, { - "range": { - "start": { "line": 0, "character": 7 }, - "end": { "line": 0, "character": 33 } - }, - "newText": "NewType" - }] - }] - }, - "isPreferred": true, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 7 }, - "end": { "line": 0, "character": 33 } - }, - "refactorName": "Extract type", - "actionName": "Extract to interface" - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_actions_imports_respects_fmt_config() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "./deno.jsonc", - json!({ - "fmt": { - "semiColons": false, - "singleQuote": true, - } - }) - .to_string(), - ); - temp_dir.write( - "file00.ts", - r#" - export interface MallardDuckConfigOptions extends DuckConfigOptions { - kind: "mallard"; - } - "#, - ); - temp_dir.write( - "file01.ts", - r#" - export interface DuckConfigOptions { - kind: string; - quacks: boolean; - } - "#, - ); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file00.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("file00.ts"), - } - })); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file01.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("file01.ts"), - } - })); - - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": temp_dir.uri().join("file00.ts").unwrap() - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 4, "character": 0 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 55 }, - "end": { "line": 1, "character": 64 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Add import from \"./file01.ts\"", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 55 }, - "end": { "line": 1, "character": 64 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": temp_dir.uri().join("file00.ts").unwrap(), - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { DuckConfigOptions } from './file01.ts'\n" - }] - }] - } - }]) - ); - let res = client.write_request( - "codeAction/resolve", - json!({ - "title": "Add all missing imports", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 55 }, - "end": { "line": 1, "character": 64 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }], - "data": { - "specifier": temp_dir.uri().join("file00.ts").unwrap(), - "fixId": "fixMissingImport" - } - }), - ); - assert_eq!( - res, - json!({ - "title": "Add all missing imports", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 55 }, - "end": { "line": 1, "character": 64 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'." - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": temp_dir.uri().join("file00.ts").unwrap(), - "version": 1 - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { DuckConfigOptions } from './file01.ts'\n" - }] - }] - }, - "data": { - "specifier": temp_dir.uri().join("file00.ts").unwrap(), - "fixId": "fixMissingImport" - } - }) - ); - - client.shutdown(); -} - -#[test] -fn lsp_quote_style_from_workspace_settings() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "file00.ts", - r#" - export interface MallardDuckConfigOptions extends DuckConfigOptions { - kind: "mallard"; - } - "#, - ); - temp_dir.write( - "file01.ts", - r#" - export interface DuckConfigOptions { - kind: string; - quacks: boolean; - } - "#, - ); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.change_configuration(json!({ - "deno": { - "enable": true, - }, - "typescript": { - "preferences": { - "quoteStyle": "single", - }, - }, - })); - - let code_action_params = json!({ - "textDocument": { - "uri": temp_dir.uri().join("file00.ts").unwrap(), - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 4, "character": 0 }, - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 56 }, - "end": { "line": 1, "character": 73 }, - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'.", - }], - "only": ["quickfix"], - }, - }); - - let res = - client.write_request("textDocument/codeAction", code_action_params.clone()); - // Expect single quotes in the auto-import. - assert_eq!( - res, - json!([{ - "title": "Add import from \"./file01.ts\"", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 56 }, - "end": { "line": 1, "character": 73 }, - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'.", - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": temp_dir.uri().join("file00.ts").unwrap(), - "version": null, - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 }, - }, - "newText": "import { DuckConfigOptions } from './file01.ts';\n", - }], - }], - }, - }]), - ); - - // It should ignore the workspace setting if a `deno.json` is present. - temp_dir.write("./deno.json", json!({}).to_string()); - client.did_change_watched_files(json!({ - "changes": [{ - "uri": temp_dir.uri().join("deno.json").unwrap(), - "type": 1, - }], - })); - - let res = client.write_request("textDocument/codeAction", code_action_params); - // Expect double quotes in the auto-import. - assert_eq!( - res, - json!([{ - "title": "Add import from \"./file01.ts\"", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 56 }, - "end": { "line": 1, "character": 73 }, - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'DuckConfigOptions'.", - }], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": temp_dir.uri().join("file00.ts").unwrap(), - "version": null, - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 }, - }, - "newText": "import { DuckConfigOptions } from \"./file01.ts\";\n", - }], - }], - }, - }]), - ); -} - -#[test] -fn lsp_code_actions_refactor_no_disabled_support() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.with_capabilities(|c| { - let doc = c.text_document.as_mut().unwrap(); - let code_action = doc.code_action.as_mut().unwrap(); - code_action.disabled_support = Some(false); - }); - }); - client.did_open( - 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 res = 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"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Move to a new file", - "kind": "refactor.move.newFile", - "isPreferred": false, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 14, "character": 0 } - }, - "refactorName": "Move to a new file", - "actionName": "Move to a new file" - } - }, { - "title": "Extract to function in module scope", - "kind": "refactor.extract.function", - "isPreferred": false, - "data": { - "specifier": "file:///a/file.ts", - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 14, "character": 0 } - }, - "refactorName": "Extract Symbol", - "actionName": "function_scope_0" - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_actions_deadlock() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let large_file_text = - fs::read_to_string(testdata_path().join("lsp").join("large_file.txt")) - .unwrap(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "javascript", - "version": 1, - "text": large_file_text, - } - })); - client.write_request( - "textDocument/semanticTokens/full", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - 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": "+++" - } - ] - }), - ); - 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": "// " - } - ] - }), - ); - 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" - } - ] - }), - ); - // 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)); - client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 609, "character": 33, } - }), - ); - client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 441, "character": 33 }, - "end": { "line": 441, "character": 42 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 441, "character": 33 }, - "end": { "line": 441, "character": 42 } - }, - "severity": 1, - "code": 7031, - "source": "deno-ts", - "message": "Binding element 'debugFlag' implicitly has an 'any' type." - }], - "only": [ "quickfix" ] - } - }), - ); - - client.read_diagnostics(); - - client.shutdown(); -} - -#[test] -fn lsp_completions() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "Deno." - } - })); - - let list = client.get_completion_list( - "file:///a/file.ts", - (0, 5), - json!({ - "triggerKind": 2, - "triggerCharacter": "." - }), - ); - assert!(!list.is_incomplete); - assert!(list.items.len() > 90); - - let res = client.write_request( - "completionItem/resolve", - json!({ - "label": "build", - "kind": 6, - "sortText": "1", - "insertTextFormat": 1, - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 5, - "name": "build", - "useCodeSnippet": false - } - } - }), - ); - assert_eq!( - res, - json!({ - "label": "build", - "kind": 6, - "detail": "const Deno.build: {\n target: string;\n arch: \"x86_64\" | \"aarch64\";\n os: \"darwin\" | \"linux\" | \"android\" | \"windows\" | \"freebsd\" | \"netbsd\" | \"aix\" | \"solaris\" | \"illumos\";\n vendor: string;\n env?: string | undefined;\n}", - "documentation": { - "kind": "markdown", - "value": "Information related to the build of the current Deno runtime.\n\nUsers are discouraged from code branching based on this information, as\nassumptions about what is available in what build environment might change\nover time. Developers should specifically sniff out the features they\nintend to use.\n\nThe intended use for the information is for logging and debugging purposes.\n\n*@category* - Runtime Environment" - }, - "sortText": "1", - "insertTextFormat": 1 - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_completions_private_fields() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": r#"class Foo { #myProperty = "value"; constructor() { this.# } }"# - } - })); - let list = client.get_completion_list( - "file:///a/file.ts", - (0, 57), - json!({ "triggerKind": 1 }), - ); - assert_eq!(list.items.len(), 1); - let item = &list.items[0]; - assert_eq!(item.label, "#myProperty"); - assert!(!list.is_incomplete); - client.shutdown(); -} - -#[test] -fn lsp_completions_optional() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 res = client.get_completion( - "file:///a/file.ts", - (8, 4), - json!({ - "triggerKind": 2, - "triggerCharacter": "." - }), - ); - assert_eq!( - json!(res), - 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 res = client.write_request( - "completionItem/resolve", - json!({ - "label": "b?", - "kind": 5, - "sortText": "1", - "filterText": "b", - "insertText": "b", - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 79, - "name": "b", - "useCodeSnippet": false - } - } - }), - ); - assert_eq!( - res, - json!({ - "label": "b?", - "kind": 5, - "detail": "(property) A.b?: string | undefined", - "documentation": { - "kind": "markdown", - "value": "" - }, - "sortText": "1", - "filterText": "b", - "insertText": "b" - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_completions_auto_import() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/b.ts", - "languageId": "typescript", - "version": 1, - "text": "export const foo = \"foo\";\n", - } - })); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "export {};\n\n", - } - })); - let list = client.get_completion_list( - "file:///a/file.ts", - (2, 0), - json!({ "triggerKind": 1 }), - ); - assert!(!list.is_incomplete); - let item = list.items.iter().find(|item| item.label == "foo"); - let Some(item) = item else { - panic!("completions items missing 'foo' symbol"); - }; - let mut item_value = serde_json::to_value(item).unwrap(); - item_value["data"]["tsc"]["data"]["exportMapKey"] = - serde_json::Value::String("".to_string()); - - let req = json!({ - "label": "foo", - "labelDetails": { - "description": "./b.ts", - }, - "kind": 6, - "sortText": "16_0", - "commitCharacters": [ - ".", - ",", - ";", - "(" - ], - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 12, - "name": "foo", - "source": "./b.ts", - "data": { - "exportName": "foo", - "exportMapKey": "", - "moduleSpecifier": "./b.ts", - "fileName": "file:///a/b.ts" - }, - "useCodeSnippet": false - } - } - }); - assert_eq!(item_value, req); - - let res = client.write_request("completionItem/resolve", req); - assert_eq!( - res, - json!({ - "label": "foo", - "labelDetails": { - "description": "./b.ts", - }, - "kind": 6, - "detail": "const foo: \"foo\"", - "documentation": { - "kind": "markdown", - "value": "" - }, - "sortText": "16_0", - "additionalTextEdits": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { foo } from \"./b.ts\";\n\n" - } - ] - }) - ); -} - -#[test] -fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import {getClient} from 'npm:@denotest/types-exports-subpaths@1/client';import chalk from 'npm:chalk@5.0';\n\n", - } - }), - ); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - ["npm:@denotest/types-exports-subpaths@1/client", "npm:chalk@5.0"], - "file:///a/file.ts", - ], - }), - ); - - // try auto-import with path - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/a.ts", - "languageId": "typescript", - "version": 1, - "text": "getClie", - } - })); - let list = client.get_completion_list( - "file:///a/a.ts", - (0, 7), - json!({ "triggerKind": 1 }), - ); - assert!(!list.is_incomplete); - let item = list - .items - .iter() - .find(|item| item.label == "getClient") - .unwrap(); - - let res = client.write_request("completionItem/resolve", item); - assert_eq!( - res, - json!({ - "label": "getClient", - "labelDetails": { - "description": "npm:@denotest/types-exports-subpaths@1/client", - }, - "kind": 3, - "detail": "function getClient(): 5", - "documentation": { - "kind": "markdown", - "value": "" - }, - "sortText": "16_1", - "additionalTextEdits": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { getClient } from \"npm:@denotest/types-exports-subpaths@1/client\";\n\n" - } - ] - }) - ); - - // try quick fix with path - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/b.ts", - "languageId": "typescript", - "version": 1, - "text": "getClient", - } - })); - let diagnostics = diagnostics - .messages_with_file_and_source("file:///a/b.ts", "deno-ts") - .diagnostics; - let res = client.write_request( - "textDocument/codeAction", - json!(json!({ - "textDocument": { - "uri": "file:///a/b.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 9 } - }, - "context": { - "diagnostics": diagnostics, - "only": ["quickfix"] - } - })), - ); - assert_eq!( - res, - json!([{ - "title": "Add import from \"npm:@denotest/types-exports-subpaths@1/client\"", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 9 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'getClient'.", - } - ], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/b.ts", - "version": 1, - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { getClient } from \"npm:@denotest/types-exports-subpaths@1/client\";\n\n" - }] - }] - } - }]) - ); - - // try auto-import without path - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/c.ts", - "languageId": "typescript", - "version": 1, - "text": "chal", - } - })); - - let list = client.get_completion_list( - "file:///a/c.ts", - (0, 4), - json!({ "triggerKind": 1 }), - ); - assert!(!list.is_incomplete); - let item = list - .items - .iter() - .find(|item| item.label == "chalk") - .unwrap(); - - let mut res = client.write_request("completionItem/resolve", item); - let obj = res.as_object_mut().unwrap(); - obj.remove("detail"); // not worth testing these - obj.remove("documentation"); - assert_eq!( - res, - json!({ - "label": "chalk", - "labelDetails": { - "description": "npm:chalk@5.0", - }, - "kind": 6, - "sortText": "16_1", - "additionalTextEdits": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import chalk from \"npm:chalk@5.0\";\n\n" - } - ] - }) - ); - - // try quick fix without path - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/d.ts", - "languageId": "typescript", - "version": 1, - "text": "chalk", - } - })); - let diagnostics = diagnostics - .messages_with_file_and_source("file:///a/d.ts", "deno-ts") - .diagnostics; - let res = client.write_request( - "textDocument/codeAction", - json!(json!({ - "textDocument": { - "uri": "file:///a/d.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 5 } - }, - "context": { - "diagnostics": diagnostics, - "only": ["quickfix"] - } - })), - ); - assert_eq!( - res, - json!([{ - "title": "Add import from \"npm:chalk@5.0\"", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 5 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'chalk'.", - } - ], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/d.ts", - "version": 1, - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import chalk from \"npm:chalk@5.0\";\n\n" - }] - }] - } - }]) - ); -} - -#[test] -fn lsp_semantic_tokens_for_disabled_module() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_with_config( - |builder| { - builder.set_deno_enable(false); - }, - json!({ "deno": { - "enable": false - } }), - ); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "const someConst = 1; someConst" - } - })); - let res = client.write_request( - "textDocument/semanticTokens/full", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!( - res, - json!({ - "data": [0, 6, 9, 7, 9, 0, 15, 9, 7, 8], - }) - ); -} - -#[test] -fn lsp_completions_auto_import_and_quick_fix_with_import_map() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let import_map = r#"{ - "imports": { - "print_hello": "http://localhost:4545/subdir/print_hello.ts", - "chalk": "npm:chalk@~5", - "nested/": "npm:/@denotest/types-exports-subpaths@1/nested/", - "types-exports-subpaths/": "npm:/@denotest/types-exports-subpaths@1/" - } - }"#; - temp_dir.write("import_map.json", import_map); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_import_map("import_map.json"); - }); - client.did_open( - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": concat!( - "import {getClient} from 'npm:@denotest/types-exports-subpaths@1/client';\n", - "import _test1 from 'npm:chalk@^5.0';\n", - "import chalk from 'npm:chalk@~5';\n", - "import chalk from 'npm:chalk@~5';\n", - "import {entryB} from 'npm:@denotest/types-exports-subpaths@1/nested/entry-b';\n", - "import {printHello} from 'print_hello';\n", - "\n", - ), - } - }), - ); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - [ - "npm:@denotest/types-exports-subpaths@1/client", - "npm:@denotest/types-exports-subpaths@1/nested/entry-b", - "npm:chalk@^5.0", - "npm:chalk@~5", - "http://localhost:4545/subdir/print_hello.ts", - ], - "file:///a/file.ts", - ], - }), - ); - - // try auto-import with path - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/a.ts", - "languageId": "typescript", - "version": 1, - "text": "getClie", - } - })); - let list = client.get_completion_list( - "file:///a/a.ts", - (0, 7), - json!({ "triggerKind": 1 }), - ); - assert!(!list.is_incomplete); - let item = list - .items - .iter() - .find(|item| item.label == "getClient") - .unwrap(); - - let res = client.write_request("completionItem/resolve", item); - assert_eq!( - res, - json!({ - "label": "getClient", - "labelDetails": { - "description": "types-exports-subpaths/client", - }, - "kind": 3, - "detail": "function getClient(): 5", - "documentation": { - "kind": "markdown", - "value": "" - }, - "sortText": "16_0", - "additionalTextEdits": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { getClient } from \"types-exports-subpaths/client\";\n\n" - } - ] - }) - ); - - // try quick fix with path - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/b.ts", - "languageId": "typescript", - "version": 1, - "text": "getClient", - } - })); - let diagnostics = diagnostics - .messages_with_file_and_source("file:///a/b.ts", "deno-ts") - .diagnostics; - let res = client.write_request( - "textDocument/codeAction", - json!(json!({ - "textDocument": { - "uri": "file:///a/b.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 9 } - }, - "context": { - "diagnostics": diagnostics, - "only": ["quickfix"] - } - })), - ); - assert_eq!( - res, - json!([{ - "title": "Add import from \"types-exports-subpaths/client\"", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 9 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'getClient'.", - } - ], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/b.ts", - "version": 1, - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { getClient } from \"types-exports-subpaths/client\";\n\n" - }] - }] - } - }]) - ); - - // try auto-import without path - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/c.ts", - "languageId": "typescript", - "version": 1, - "text": "chal", - } - })); - - let list = client.get_completion_list( - "file:///a/c.ts", - (0, 4), - json!({ "triggerKind": 1 }), - ); - assert!(!list.is_incomplete); - let item = list - .items - .iter() - .find(|item| item.label == "chalk") - .unwrap(); - - let mut res = client.write_request("completionItem/resolve", item); - let obj = res.as_object_mut().unwrap(); - obj.remove("detail"); // not worth testing these - obj.remove("documentation"); - assert_eq!( - res, - json!({ - "label": "chalk", - "labelDetails": { - "description": "chalk", - }, - "kind": 6, - "sortText": "16_0", - "additionalTextEdits": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import chalk from \"chalk\";\n\n" - } - ] - }) - ); - - // try quick fix without path - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/d.ts", - "languageId": "typescript", - "version": 1, - "text": "chalk", - } - })); - let diagnostics = diagnostics - .messages_with_file_and_source("file:///a/d.ts", "deno-ts") - .diagnostics; - let res = client.write_request( - "textDocument/codeAction", - json!(json!({ - "textDocument": { - "uri": "file:///a/d.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 5 } - }, - "context": { - "diagnostics": diagnostics, - "only": ["quickfix"] - } - })), - ); - assert_eq!( - res, - json!([{ - "title": "Add import from \"chalk\"", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 5 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'chalk'.", - } - ], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/d.ts", - "version": 1, - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import chalk from \"chalk\";\n\n" - }] - }] - } - }]) - ); - - // try auto-import with http import map - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/e.ts", - "languageId": "typescript", - "version": 1, - "text": "printH", - } - })); - - let list = client.get_completion_list( - "file:///a/e.ts", - (0, 6), - json!({ "triggerKind": 1 }), - ); - assert!(!list.is_incomplete); - let item = list - .items - .iter() - .find(|item| item.label == "printHello") - .unwrap(); - - let mut res = client.write_request("completionItem/resolve", item); - let obj = res.as_object_mut().unwrap(); - obj.remove("detail"); // not worth testing these - obj.remove("documentation"); - assert_eq!( - res, - json!({ - "label": "printHello", - "labelDetails": { - "description": "print_hello", - }, - "kind": 3, - "sortText": "16_0", - "additionalTextEdits": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { printHello } from \"print_hello\";\n\n" - } - ] - }) - ); - - // try quick fix with http import - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/f.ts", - "languageId": "typescript", - "version": 1, - "text": "printHello", - } - })); - let diagnostics = diagnostics - .messages_with_file_and_source("file:///a/f.ts", "deno-ts") - .diagnostics; - let res = client.write_request( - "textDocument/codeAction", - json!(json!({ - "textDocument": { - "uri": "file:///a/f.ts" - }, - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 10 } - }, - "context": { - "diagnostics": diagnostics, - "only": ["quickfix"] - } - })), - ); - assert_eq!( - res, - json!([{ - "title": "Add import from \"print_hello\"", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 10 } - }, - "severity": 1, - "code": 2304, - "source": "deno-ts", - "message": "Cannot find name 'printHello'.", - } - ], - "edit": { - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/f.ts", - "version": 1, - }, - "edits": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { printHello } from \"print_hello\";\n\n" - }] - }] - } - }]) - ); - - // try auto-import with npm package with sub-path on value side of import map - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/nested_path.ts", - "languageId": "typescript", - "version": 1, - "text": "entry", - } - })); - let list = client.get_completion_list( - "file:///a/nested_path.ts", - (0, 5), - json!({ "triggerKind": 1 }), - ); - assert!(!list.is_incomplete); - let item = list - .items - .iter() - .find(|item| item.label == "entryB") - .unwrap(); - - let res = client.write_request("completionItem/resolve", item); - assert_eq!( - res, - json!({ - "label": "entryB", - "labelDetails": { - "description": "nested/entry-b", - }, - "kind": 3, - "detail": "function entryB(): \"b\"", - "documentation": { - "kind": "markdown", - "value": "" - }, - "sortText": "16_0", - "additionalTextEdits": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "import { entryB } from \"nested/entry-b\";\n\n" - } - ] - }) - ); -} - -#[test] -fn lsp_completions_snippet() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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 list = client.get_completion_list( - "file:///a/a.tsx", - (5, 13), - json!({ "triggerKind": 1 }), - ); - 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 - } - } - } - ] - }) - ); - - let res = 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 - } - } - }), - ); - assert_eq!( - res, - 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 context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.with_capabilities(|c| { - let doc = c.text_document.as_mut().unwrap(); - doc.completion = None; - }); - }); - client.did_open( - 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 list = client.get_completion_list( - "file:///a/a.tsx", - (5, 13), - json!({ "triggerKind": 1 }), - ); - 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 - } - } - } - ] - }) - ); -} - -#[test] -fn lsp_completions_npm() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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", - } - }), - ); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - ["npm:@denotest/cjs-default-export", "npm:chalk"], - "file:///a/file.ts", - ], - }), - ); - - // 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." - } - ] - }), - ); - client.read_diagnostics(); - - let list = client.get_completion_list( - "file:///a/file.ts", - (2, 11), - json!({ - "triggerKind": 2, - "triggerCharacter": "." - }), - ); - 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")); - - let res = client.write_request( - "completionItem/resolve", - json!({ - "label": "MyClass", - "kind": 6, - "sortText": "1", - "insertTextFormat": 1, - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 69, - "name": "MyClass", - "useCodeSnippet": false - } - } - }), - ); - assert_eq!( - res, - json!({ - "label": "MyClass", - "kind": 6, - "sortText": "1", - "insertTextFormat": 1, - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 69, - "name": "MyClass", - "useCodeSnippet": false - } - } - }) - ); - - // 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." - } - ] - }), - ); - client.read_diagnostics(); - - let list = client.get_completion_list( - "file:///a/file.ts", - (2, 6), - json!({ - "triggerKind": 2, - "triggerCharacter": "." - }), - ); - assert!(!list.is_incomplete); - assert!(list.items.iter().any(|i| i.label == "green")); - assert!(list.items.iter().any(|i| i.label == "red")); - - client.shutdown(); -} - -#[test] -fn lsp_npm_specifier_unopened_file() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - // 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()) - .piped_output() - .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(); - client.did_open(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." - } - ] - }), - ); - client.read_diagnostics(); - - // now ensure completions work - let list = client.get_completion_list( - main_url, - (2, 6), - json!({ - "triggerKind": 2, - "triggerCharacter": "." - }), - ); - assert!(!list.is_incomplete); - assert_eq!(list.items.len(), 63); - assert!(list.items.iter().any(|i| i.label == "ansi256")); -} - -#[test] -fn lsp_completions_node_specifier() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import fs from 'node:non-existent';\n\n", - } - })); - - let non_existent_diagnostics = diagnostics - .messages_with_file_and_source("file:///a/file.ts", "deno") - .diagnostics - .into_iter() - .filter(|d| { - d.code == Some(lsp::NumberOrString::String("resolver-error".to_string())) - }) - .collect::<Vec<_>>(); - assert_eq!( - json!(non_existent_diagnostics), - json!([ - { - "range": { - "start": { "line": 0, "character": 15 }, - "end": { "line": 0, "character": 34 }, - }, - "severity": 1, - "code": "resolver-error", - "source": "deno", - "message": "Unknown Node built-in module: non-existent" - } - ]) - ); - - // update to have fs import - client.write_notification( - "textDocument/didChange", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { "line": 0, "character": 16 }, - "end": { "line": 0, "character": 33 }, - }, - "text": "fs" - } - ] - }), - ); - let diagnostics = client.read_diagnostics(); - let diagnostics = diagnostics - .messages_with_file_and_source("file:///a/file.ts", "deno") - .diagnostics - .into_iter() - .filter(|d| { - d.code - == Some(lsp::NumberOrString::String( - "import-node-prefix-missing".to_string(), - )) - }) - .collect::<Vec<_>>(); - - // get the quick fixes - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 0, "character": 16 }, - "end": { "line": 0, "character": 18 }, - }, - "context": { - "diagnostics": json!(diagnostics), - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Update specifier to node:fs", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 15 }, - "end": { "line": 0, "character": 19 } - }, - "severity": 1, - "code": "import-node-prefix-missing", - "source": "deno", - "message": "Relative import path \"fs\" not prefixed with / or ./ or ../\nIf you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:fs\").", - "data": { - "specifier": "fs" - }, - } - ], - "edit": { - "changes": { - "file:///a/file.ts": [ - { - "range": { - "start": { "line": 0, "character": 15 }, - "end": { "line": 0, "character": 19 } - }, - "newText": "\"node:fs\"" - } - ] - } - } - }]) - ); - - // update to have node:fs import - client.write_notification( - "textDocument/didChange", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 3, - }, - "contentChanges": [ - { - "range": { - "start": { "line": 0, "character": 15 }, - "end": { "line": 0, "character": 19 }, - }, - "text": "\"node:fs\"", - } - ] - }), - ); - - let diagnostics = client.read_diagnostics(); - let cache_diagnostics = diagnostics - .messages_with_file_and_source("file:///a/file.ts", "deno") - .diagnostics - .into_iter() - .filter(|d| { - d.code == Some(lsp::NumberOrString::String("no-cache-npm".to_string())) - }) - .collect::<Vec<_>>(); - - assert_eq!( - json!(cache_diagnostics), - json!([ - { - "range": { - "start": { "line": 0, "character": 15 }, - "end": { "line": 0, "character": 24 } - }, - "data": { - "specifier": "npm:@types/node", - }, - "severity": 1, - "code": "no-cache-npm", - "source": "deno", - "message": "Uncached or missing npm package: @types/node" - } - ]) - ); - - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [["npm:@types/node"], "file:///a/file.ts"], - }), - ); - - client.write_notification( - "textDocument/didChange", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 4 - }, - "contentChanges": [ - { - "range": { - "start": { "line": 2, "character": 0 }, - "end": { "line": 2, "character": 0 } - }, - "text": "fs." - } - ] - }), - ); - client.read_diagnostics(); - - let list = client.get_completion_list( - "file:///a/file.ts", - (2, 3), - json!({ - "triggerKind": 2, - "triggerCharacter": "." - }), - ); - assert!(!list.is_incomplete); - assert!(list.items.iter().any(|i| i.label == "writeFile")); - assert!(list.items.iter().any(|i| i.label == "writeFileSync")); - - client.shutdown(); -} - -#[test] -fn lsp_completions_registry() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.add_test_server_suggestions(); - }); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"http://localhost:4545/x/a@\"" - } - })); - let list = client.get_completion_list( - "file:///a/file.ts", - (0, 46), - json!({ - "triggerKind": 2, - "triggerCharacter": "@" - }), - ); - assert!(!list.is_incomplete); - assert_eq!(list.items.len(), 3); - - let res = client.write_request( - "completionItem/resolve", - json!({ - "label": "v2.0.0", - "kind": 19, - "detail": "(version)", - "sortText": "0000000003", - "filterText": "http://localhost:4545/x/a@v2.0.0", - "textEdit": { - "range": { - "start": { "line": 0, "character": 20 }, - "end": { "line": 0, "character": 46 } - }, - "newText": "http://localhost:4545/x/a@v2.0.0" - } - }), - ); - assert_eq!( - res, - json!({ - "label": "v2.0.0", - "kind": 19, - "detail": "(version)", - "sortText": "0000000003", - "filterText": "http://localhost:4545/x/a@v2.0.0", - "textEdit": { - "range": { - "start": { "line": 0, "character": 20 }, - "end": { "line": 0, "character": 46 } - }, - "newText": "http://localhost:4545/x/a@v2.0.0" - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_completions_registry_empty() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.add_test_server_suggestions(); - }); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"\"" - } - })); - let res = client.get_completion( - "file:///a/file.ts", - (0, 20), - json!({ - "triggerKind": 2, - "triggerCharacter": "\"" - }), - ); - assert_eq!( - json!(res), - json!({ - "isIncomplete": false, - "items": [{ - "label": ".", - "kind": 19, - "detail": "(local)", - "sortText": "1", - "insertText": ".", - "commitCharacters": ["\"", "'"] - }, { - "label": "..", - "kind": 19, - "detail": "(local)", - "sortText": "1", - "insertText": "..", - "commitCharacters": ["\"", "'" ] - }, { - "label": "http://localhost:4545", - "kind": 19, - "detail": "(registry)", - "sortText": "2", - "textEdit": { - "range": { - "start": { "line": 0, "character": 20 }, - "end": { "line": 0, "character": 20 } - }, - "newText": "http://localhost:4545" - }, - "commitCharacters": ["\"", "'"] - }] - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_auto_discover_registry() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"http://localhost:4545/x/a@\"" - } - })); - client.get_completion( - "file:///a/file.ts", - (0, 46), - json!({ - "triggerKind": 2, - "triggerCharacter": "@" - }), - ); - let (method, res) = client.read_notification(); - assert_eq!(method, "deno/registryState"); - assert_eq!( - res, - Some(json!({ - "origin": "http://localhost:4545", - "suggestions": true, - })) - ); - client.shutdown(); -} - -#[test] -fn lsp_cache_location() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_cache(".cache").add_test_server_suggestions(); - }); - - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file_01.ts", - "languageId": "typescript", - "version": 1, - "text": "export const a = \"a\";\n", - } - })); - let diagnostics = - client.did_open(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// @deno-types=\"http://127.0.0.1:4545/type_definitions/foo.d.ts\"\nimport * as b from \"http://127.0.0.1:4545/type_definitions/foo.js\";\nimport * as c from \"http://127.0.0.1:4545/subdir/type_reference.js\";\nimport * as d from \"http://127.0.0.1:4545/subdir/mod1.ts\";\nimport * as e from \"data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=\";\nimport * as f from \"./file_01.ts\";\nimport * as g from \"http://localhost:4545/x/a/mod.ts\";\n\nconsole.log(a, b, c, d, e, f, g);\n" - } - })); - assert_eq!(diagnostics.all().len(), 6); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [[], "file:///a/file.ts"], - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 0, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http​://127.0.0.1:4545/xTypeScriptTypes.d.ts\n" - }, - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 62 } - } - }) - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 7, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://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()); // not created because no emitting has occurred - client.shutdown(); -} - -/// 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 context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder - .set_suggest_imports_hosts(vec![ - ("http://localhost:4545/".to_string(), true), - ("https://localhost:5545/".to_string(), true), - ]) - .set_tls_certificate(""); - }); - - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file_01.ts", - "languageId": "typescript", - "version": 1, - "text": "export const a = \"a\";\n", - } - })); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"https://localhost:5545/xTypeScriptTypes.js\";\n// @deno-types=\"https://localhost:5545/type_definitions/foo.d.ts\"\nimport * as b from \"https://localhost:5545/type_definitions/foo.js\";\nimport * as c from \"https://localhost:5545/subdir/type_reference.js\";\nimport * as d from \"https://localhost:5545/subdir/mod1.ts\";\nimport * as e from \"data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=\";\nimport * as f from \"./file_01.ts\";\nimport * as g from \"http://localhost:4545/x/a/mod.ts\";\n\nconsole.log(a, b, c, d, e, f, g);\n" - } - })); - let diagnostics = diagnostics.all(); - assert_eq!(diagnostics.len(), 6); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [[], "file:///a/file.ts"], - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 0, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: https​://localhost:5545/xTypeScriptTypes.js\n" - }, - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 63 } - } - }) - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts", - }, - "position": { "line": 7, "character": 28 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://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 } - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_diagnostics_warn_redirect() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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", - }, - }), - ); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - ["http://127.0.0.1:4545/x_deno_warning.js"], - "file:///a/file.ts", - ], - }), - ); - let diagnostics = client.read_diagnostics(); - assert_eq!( - diagnostics.messages_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), - } - ); - client.shutdown(); -} - -#[test] -fn lsp_redirect_quick_fix() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open( - 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", - }, - }), - ); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - ["http://127.0.0.1:4545/x_deno_warning.js"], - "file:///a/file.ts", - ], - }), - ); - let diagnostics = client - .read_diagnostics() - .messages_with_source("deno") - .diagnostics; - let res = 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"] - } - })), - ); - assert_eq!( - res, - json!([{ - "title": "Update specifier to its redirected specifier.", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 60 } - }, - "severity": 3, - "code": "redirect", - "source": "deno", - "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\".", - "data": { - "specifier": "http://127.0.0.1:4545/x_deno_warning.js", - "redirect": "http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js" - } - } - ], - "edit": { - "changes": { - "file:///a/file.ts": [ - { - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 60 } - }, - "newText": "\"http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js\"" - } - ] - } - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_diagnostics_deprecated() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "/** @deprecated */\nexport const a = \"a\";\n\na;\n", - }, - })); - assert_eq!( - json!(diagnostics.all_messages()), - json!([{ - "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": [ - { - "location": { - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 0, - "character": 4, - }, - "end": { - "line": 0, - "character": 16, - }, - }, - }, - "message": "The declaration was marked as deprecated here.", - }, - ], - "tags": [2] - } - ], - "version": 1 - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_diagnostics_deno_types() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client - .did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "/// <reference types=\"https://example.com/a/b.d.ts\" />\n/// <reference path=\"https://example.com/a/c.ts\"\n\n// @deno-types=https://example.com/a/d.d.ts\nimport * as d from \"https://example.com/a/d.js\";\n\n// @deno-types=\"https://example.com/a/e.d.ts\"\nimport * as e from \"https://example.com/a/e.js\";\n\nconsole.log(d, e);\n" - } - }), - ); - - client.write_request( - "textDocument/documentSymbol", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - } - }), - ); - assert_eq!(diagnostics.all().len(), 5); - client.shutdown(); -} - -#[test] -fn lsp_diagnostics_refresh_dependents() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file_00.ts", - "languageId": "typescript", - "version": 1, - "text": "export const a = \"a\";\n", - }, - })); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file_01.ts", - "languageId": "typescript", - "version": 1, - "text": "export * from \"./file_00.ts\";\n", - }, - })); - let diagnostics = client.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 - .messages_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 - 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" - } - ] - }), - ); - let diagnostics = client.read_diagnostics(); - assert_eq!(diagnostics.all().len(), 0); // no diagnostics now - - client.shutdown(); - assert_eq!(client.queue_len(), 0); -} - -// Regression test for https://github.com/denoland/deno/issues/10897. -#[test] -fn lsp_ts_diagnostics_refresh_on_lsp_version_reset() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("file.ts", r#"Deno.readTextFileSync(1);"#); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("file.ts"), - }, - })); - assert_eq!(diagnostics.all().len(), 1); - client.write_notification( - "textDocument/didClose", - json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - }, - }), - ); - temp_dir.remove_file("file.ts"); - // VSCode opens with `version: 1` again because the file was deleted. Ensure - // diagnostics are still refreshed. - client.did_open_raw(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": "", - }, - })); - temp_dir.write("file.ts", r#""#); - client.did_save(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.ts").unwrap(), - }, - })); - let diagnostics = client.read_diagnostics(); - assert_eq!(diagnostics.all(), vec![]); - client.shutdown(); -} - -#[test] -fn lsp_jupyter_diagnostics() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "deno-notebook-cell:/a/file.ts#abc", - "languageId": "typescript", - "version": 1, - "text": "Deno.readTextFileSync(1234);", - }, - })); - assert_eq!( - json!(diagnostics.all_messages()), - json!([ - { - "uri": "deno-notebook-cell:/a/file.ts#abc", - "diagnostics": [ - { - "range": { - "start": { - "line": 0, - "character": 22, - }, - "end": { - "line": 0, - "character": 26, - }, - }, - "severity": 1, - "code": 2345, - "source": "deno-ts", - "message": "Argument of type 'number' is not assignable to parameter of type 'string | URL'.", - }, - ], - "version": 1, - }, - ]) - ); - client.shutdown(); -} - -#[test] -fn lsp_untitled_file_diagnostics() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "untitled:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "Deno.readTextFileSync(1234);", - }, - })); - assert_eq!( - json!(diagnostics.all_messages()), - json!([ - { - "uri": "untitled:///a/file.ts", - "diagnostics": [ - { - "range": { - "start": { - "line": 0, - "character": 22, - }, - "end": { - "line": 0, - "character": 26, - }, - }, - "severity": 1, - "code": 2345, - "source": "deno-ts", - "message": "Argument of type 'number' is not assignable to parameter of type 'string | URL'.", - }, - ], - "version": 1, - }, - ]) - ); - client.shutdown(); -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PerformanceAverage { - pub name: String, - pub count: u32, - pub average_duration: u32, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct PerformanceAverages { - averages: Vec<PerformanceAverage>, -} - -#[test] -fn lsp_performance() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Deno.args);\n" - } - })); - client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { "line": 0, "character": 19 } - }), - ); - let res = client.write_request_with_res_as::<PerformanceAverages>( - "deno/performance", - json!(null), - ); - let mut averages = res - .averages - .iter() - .map(|a| a.name.as_str()) - .collect::<Vec<_>>(); - averages.sort(); - assert_eq!( - averages, - vec![ - "lsp.did_open", - "lsp.hover", - "lsp.initialize", - "lsp.testing_update", - "lsp.update_cache", - "lsp.update_diagnostics_deps", - "lsp.update_diagnostics_lint", - "lsp.update_diagnostics_ts", - "lsp.update_import_map", - "lsp.update_registries", - "lsp.update_tsconfig", - "tsc.host.$configure", - "tsc.host.$getAssets", - "tsc.host.$getDiagnostics", - "tsc.host.$getSupportedCodeFixes", - "tsc.host.getQuickInfoAtPosition", - "tsc.op.op_is_node_file", - "tsc.op.op_load", - "tsc.op.op_project_version", - "tsc.op.op_script_names", - "tsc.op.op_script_version", - "tsc.request.$configure", - "tsc.request.$getAssets", - "tsc.request.$getSupportedCodeFixes", - "tsc.request.getQuickInfoAtPosition", - ] - ); - client.shutdown(); -} - -#[test] -fn lsp_format_no_changes() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console;\n" - } - })); - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - assert_eq!(res, json!(null)); - client.assert_no_notification("window/showMessage"); - client.shutdown(); -} - -#[test] -fn lsp_format_error() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console test test\n" - } - })); - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - assert_eq!(res, json!(null)); - client.shutdown(); -} - -#[test] -fn lsp_format_mbc() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "const bar = '👍🇺🇸😃'\nconsole.log('hello deno')\n" - } - })); - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - assert_eq!( - res, - json!([{ - "range": { - "start": { "line": 0, "character": 12 }, - "end": { "line": 0, "character": 13 } - }, - "newText": "\"" - }, { - "range": { - "start": { "line": 0, "character": 21 }, - "end": { "line": 0, "character": 22 } - }, - "newText": "\";" - }, { - "range": { - "start": { "line": 1, "character": 12 }, - "end": { "line": 1, "character": 13 } - }, - "newText": "\"" - }, { - "range": { - "start": { "line": 1, "character": 23 }, - "end": { "line": 1, "character": 25 } - }, - "newText": "\");" - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_format_exclude_with_config() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - temp_dir.write( - "deno.fmt.jsonc", - r#"{ - "fmt": { - "files": { - "exclude": ["ignored.ts"] - }, - "options": { - "useTabs": true, - "lineWidth": 40, - "indentWidth": 8, - "singleQuote": true, - "proseWrap": "always" - } - } - }"#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.fmt.jsonc"); - }); - - let file_uri = temp_dir.uri().join("ignored.ts").unwrap(); - client.did_open(json!({ - "textDocument": { - "uri": file_uri, - "languageId": "typescript", - "version": 1, - "text": "function myFunc(){}" - } - })); - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": file_uri - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - assert_eq!(res, json!(null)); - client.shutdown(); -} - -#[test] -fn lsp_format_exclude_default_config() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - temp_dir.write( - "deno.fmt.jsonc", - r#"{ - "fmt": { - "files": { - "exclude": ["ignored.ts"] - }, - "options": { - "useTabs": true, - "lineWidth": 40, - "indentWidth": 8, - "singleQuote": true, - "proseWrap": "always" - } - } - }"#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.fmt.jsonc"); - }); - - let file_uri = temp_dir.uri().join("ignored.ts").unwrap(); - client.did_open(json!({ - "textDocument": { - "uri": file_uri, - "languageId": "typescript", - "version": 1, - "text": "function myFunc(){}" - } - })); - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": file_uri - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - assert_eq!(res, json!(null)); - client.shutdown(); -} - -#[test] -fn lsp_format_json() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir_path = context.temp_dir().path(); - // Also test out using a non-json file extension here. - // What should matter is the language identifier. - let lock_file_path = temp_dir_path.join("file.lock"); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": lock_file_path.uri_file(), - "languageId": "json", - "version": 1, - "text": "{\"key\":\"value\"}" - } - })); - - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": lock_file_path.uri_file(), - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - - assert_eq!( - res, - 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" - } - ]) - ); - client.shutdown(); -} - -#[test] -fn lsp_json_no_diagnostics() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.json", - "languageId": "json", - "version": 1, - "text": "{\"key\":\"value\"}" - } - })); - - let res = client.write_request( - "textDocument/semanticTokens/full", - json!({ - "textDocument": { - "uri": "file:///a/file.json" - } - }), - ); - assert_eq!(res, json!(null)); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.json" - }, - "position": { "line": 0, "character": 3 } - }), - ); - assert_eq!(res, json!(null)); - - client.shutdown(); -} - -#[test] -fn lsp_json_import_with_query_string() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("data.json", r#"{"k": "v"}"#); - temp_dir.write( - "main.ts", - r#" - import data from "./data.json?1" with { type: "json" }; - console.log(data); - "#, - ); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("data.json").unwrap(), - "languageId": "json", - "version": 1, - "text": temp_dir.read_to_string("data.json"), - } - })); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("main.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": temp_dir.read_to_string("main.ts"), - } - })); - assert_eq!(diagnostics.all(), vec![]); - client.shutdown(); -} - -#[test] -fn lsp_format_markdown() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let markdown_file = context.temp_dir().path().join("file.md"); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": markdown_file.uri_file(), - "languageId": "markdown", - "version": 1, - "text": "# Hello World" - } - })); - - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": markdown_file.uri_file() - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - - assert_eq!( - res, - 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" - } - ]) - ); - client.shutdown(); -} - -#[test] -fn lsp_format_with_config() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.fmt.jsonc", - r#"{ - "fmt": { - "options": { - "useTabs": true, - "lineWidth": 40, - "indentWidth": 8, - "singleQuote": true, - "proseWrap": "always" - } - } - } - "#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.fmt.jsonc"); - }); - - let ts_file = temp_dir.path().join("file.ts"); - client - .did_open( - json!({ - "textDocument": { - "uri": ts_file.uri_file(), - "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}" - } - }), - ); - - // The options below should be ignored in favor of configuration from config file. - let res = client.write_request( - "textDocument/formatting", - json!({ - "textDocument": { - "uri": ts_file.uri_file() - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - }), - ); - - assert_eq!( - res, - 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" - }] - ) - ); - client.shutdown(); -} - -#[test] -fn lsp_markdown_no_diagnostics() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.md", - "languageId": "markdown", - "version": 1, - "text": "# Hello World" - } - })); - - let res = client.write_request( - "textDocument/semanticTokens/full", - json!({ - "textDocument": { - "uri": "file:///a/file.md" - } - }), - ); - assert_eq!(res, json!(null)); - - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.md" - }, - "position": { "line": 0, "character": 3 } - }), - ); - assert_eq!(res, json!(null)); - - client.shutdown(); -} - -#[test] -fn lsp_configuration_did_change() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"http://localhost:4545/x/a@\"" - } - })); - client.change_configuration(json!({ "deno": { - "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, - } })); - - let list = client.get_completion_list( - "file:///a/file.ts", - (0, 46), - json!({ - "triggerKind": 2, - "triggerCharacter": "@" - }), - ); - assert!(!list.is_incomplete); - assert_eq!(list.items.len(), 3); - - let res = client.write_request( - "completionItem/resolve", - json!({ - "label": "v2.0.0", - "kind": 19, - "detail": "(version)", - "sortText": "0000000003", - "filterText": "http://localhost:4545/x/a@v2.0.0", - "textEdit": { - "range": { - "start": { "line": 0, "character": 20 }, - "end": { "line": 0, "character": 46 } - }, - "newText": "http://localhost:4545/x/a@v2.0.0" - } - }), - ); - assert_eq!( - res, - json!({ - "label": "v2.0.0", - "kind": 19, - "detail": "(version)", - "sortText": "0000000003", - "filterText": "http://localhost:4545/x/a@v2.0.0", - "textEdit": { - "range": { - "start": { "line": 0, "character": 20 }, - "end": { "line": 0, "character": 46 } - }, - "newText": "http://localhost:4545/x/a@v2.0.0" - } - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_completions_complete_function_calls() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "[]." - } - })); - client.change_configuration(json!({ - "deno": { - "enable": true, - }, - "typescript": { - "suggest": { - "completeFunctionCalls": true, - }, - }, - })); - - let list = client.get_completion_list( - "file:///a/file.ts", - (0, 3), - json!({ - "triggerKind": 2, - "triggerCharacter": ".", - }), - ); - assert!(!list.is_incomplete); - - let res = client.write_request( - "completionItem/resolve", - json!({ - "label": "map", - "kind": 2, - "sortText": "1", - "insertTextFormat": 1, - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 3, - "name": "map", - "useCodeSnippet": true - } - } - }), - ); - assert_eq!( - res, - json!({ - "label": "map", - "kind": 2, - "detail": "(method) Array<never>.map<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any): U[]", - "documentation": { - "kind": "markdown", - "value": "Calls a defined callback function on each element of an array, and returns an array that contains the results.\n\n*@param* - callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.*@param* - thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value." - }, - "sortText": "1", - "insertText": "map(${1:callbackfn})", - "insertTextFormat": 2, - }) - ); - client.shutdown(); -} - -#[test] -fn lsp_workspace_symbol() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "export class A {\n fieldA: string;\n fieldB: string;\n}\n", - } - })); - client.did_open(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 mut res = client.write_request( - "workspace/symbol", - json!({ - "query": "field" - }), - ); - - // Replace `range` fields with `null` values. These are not important - // for assertion and require to be updated if we change unstable APIs. - for obj in res.as_array_mut().unwrap().iter_mut() { - *obj - .as_object_mut() - .unwrap() - .get_mut("location") - .unwrap() - .as_object_mut() - .unwrap() - .get_mut("range") - .unwrap() = Value::Null; - } - - assert_eq!( - res, - json!([ - { - "name": "fieldA", - "kind": 8, - "location": { - "uri": "file:///a/file.ts", - "range": null, - }, - "containerName": "A" - }, - { - "name": "fieldB", - "kind": 8, - "location": { - "uri": "file:///a/file.ts", - "range": null, - }, - "containerName": "A" - }, - { - "name": "fieldC", - "kind": 8, - "location": { - "uri": "file:///a/file_01.ts", - "range": null, - }, - "containerName": "B" - }, - { - "name": "fieldD", - "kind": 8, - "location": { - "uri": "file:///a/file_01.ts", - "range": null, - }, - "containerName": "B" - }, - { - "name": "fields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "CalendarProtocol" - }, - { - "name": "fields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Calendar" - }, - { - "name": "ClassFieldDecoratorContext", - "kind": 11, - "location": { - "uri": "deno:/asset/lib.decorators.d.ts", - "range": null, - }, - "containerName": "" - }, - { - "name": "dateFromFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "CalendarProtocol" - }, - { - "name": "dateFromFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Calendar" - }, - { - "name": "getISOFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "PlainDate" - }, - { - "name": "getISOFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "PlainDateTime" - }, - { - "name": "getISOFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "PlainMonthDay" - }, - { - "name": "getISOFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "PlainTime" - }, - { - "name": "getISOFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "PlainYearMonth" - }, - { - "name": "getISOFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "ZonedDateTime" - }, - { - "name": "mergeFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "CalendarProtocol" - }, - { - "name": "mergeFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Calendar" - }, - { - "name": "monthDayFromFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "CalendarProtocol" - }, - { - "name": "monthDayFromFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Calendar" - }, - { - "name": "PlainDateISOFields", - "kind": 5, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Temporal" - }, - { - "name": "PlainDateTimeISOFields", - "kind": 5, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Temporal" - }, - { - "name": "PlainTimeISOFields", - "kind": 5, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Temporal" - }, - { - "name": "yearMonthFromFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "CalendarProtocol" - }, - { - "name": "yearMonthFromFields", - "kind": 6, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Calendar" - }, - { - "name": "ZonedDateTimeISOFields", - "kind": 5, - "location": { - "uri": "deno:/asset/lib.deno.unstable.d.ts", - "range": null, - }, - "containerName": "Temporal" - } - ]) - ); - client.shutdown(); -} - -#[test] -fn lsp_code_actions_ignore_lint() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "let message = 'Hello, Deno!';\nconsole.log(message);\n" - } - })); - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 1, "character": 5 }, - "end": { "line": 1, "character": 12 } - }, - "context": { - "diagnostics": [ - { - "range": { - "start": { "line": 1, "character": 5 }, - "end": { "line": 1, "character": 12 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'message' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - } - ], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Disable prefer-const for this line", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 5 }, - "end": { "line": 1, "character": 12 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'message' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - }], - "edit": { - "changes": { - "file:///a/file.ts": [{ - "range": { - "start": { "line": 1, "character": 0 }, - "end": { "line": 1, "character": 0 } - }, - "newText": "// deno-lint-ignore prefer-const\n" - }] - } - } - }, { - "title": "Disable prefer-const for the entire file", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 5 }, - "end": { "line": 1, "character": 12 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'message' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - }], - "edit": { - "changes": { - "file:///a/file.ts": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "// deno-lint-ignore-file prefer-const\n" - }] - } - } - }, { - "title": "Ignore lint errors for the entire file", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 1, "character": 5 }, - "end": { "line": 1, "character": 12 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'message' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - }], - "edit": { - "changes": { - "file:///a/file.ts": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "// deno-lint-ignore-file\n" - }] - } - } - }]) - ); - client.shutdown(); -} - -/// This test exercises updating an existing deno-lint-ignore-file comment. -#[test] -fn lsp_code_actions_update_ignore_lint() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(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 res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { "line": 3, "character": 5 }, - "end": { "line": 3, "character": 15 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 3, "character": 5 }, - "end": { "line": 3, "character": 15 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'snake_case' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - }], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Disable prefer-const for this line", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 3, "character": 5 }, - "end": { "line": 3, "character": 15 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'snake_case' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - }], - "edit": { - "changes": { - "file:///a/file.ts": [{ - "range": { - "start": { "line": 3, "character": 0 }, - "end": { "line": 3, "character": 0 } - }, - "newText": "// deno-lint-ignore prefer-const\n" - }] - } - } - }, { - "title": "Disable prefer-const for the entire file", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 3, "character": 5 }, - "end": { "line": 3, "character": 15 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'snake_case' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - }], - "edit": { - "changes": { - "file:///a/file.ts": [{ - "range": { - "start": { "line": 1, "character": 34 }, - "end": { "line": 1, "character": 34 } - }, - "newText": " prefer-const" - }] - } - } - }, { - "title": "Ignore lint errors for the entire file", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 3, "character": 5 }, - "end": { "line": 3, "character": 15 } - }, - "severity": 1, - "code": "prefer-const", - "source": "deno-lint", - "message": "'snake_case' is never reassigned\nUse 'const' instead", - "relatedInformation": [] - }], - "edit": { - "changes": { - "file:///a/file.ts": [{ - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 0, "character": 0 } - }, - "newText": "// deno-lint-ignore-file\n" - }] - } - } - }]) - ); - client.shutdown(); -} - -#[test] -fn lsp_lint_with_config() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - temp_dir.write( - "deno.lint.jsonc", - r#"{ - "lint": { - "rules": { - "exclude": ["camelcase"], - "include": ["ban-untagged-todo"], - "tags": [] - } - } - } - "#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.lint.jsonc"); - }); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "// TODO: fixme\nexport async function non_camel_case() {\nconsole.log(\"finished!\")\n}" - } - })); - let diagnostics = diagnostics.all(); - assert_eq!(diagnostics.len(), 1); - assert_eq!( - diagnostics[0].code, - Some(lsp::NumberOrString::String("ban-untagged-todo".to_string())) - ); - client.shutdown(); -} - -#[test] -fn lsp_lint_exclude_with_config() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - temp_dir.write( - "deno.lint.jsonc", - r#"{ - "lint": { - "files": { - "exclude": ["ignored.ts"] - }, - "rules": { - "exclude": ["camelcase"], - "include": ["ban-untagged-todo"], - "tags": [] - } - } - }"#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_config("./deno.lint.jsonc"); - }); - - let diagnostics = client.did_open( - 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.all(); - assert_eq!(diagnostics, Vec::new()); - client.shutdown(); -} - -#[test] -fn lsp_jsx_import_source_pragma() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(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>; -} -", - } - })); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [ - ["http://127.0.0.1:4545/jsx/jsx-runtime"], - "file:///a/file.tsx", - ], - }), - ); - let res = client.write_request( - "textDocument/hover", - json!({ - "textDocument": { - "uri": "file:///a/file.tsx" - }, - "position": { "line": 0, "character": 25 } - }), - ); - assert_eq!( - res, - json!({ - "contents": { - "kind": "markdown", - "value": "**Resolved Dependency**\n\n**Code**: http​://localhost:4545/jsx/jsx-runtime\n", - }, - "range": { - "start": { "line": 0, "character": 21 }, - "end": { "line": 0, "character": 46 } - } - }) - ); - client.shutdown(); -} - -#[ignore = "https://github.com/denoland/deno/issues/21770"] -#[test] -fn lsp_jsx_import_source_config_file_automatic_cache() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.json", - json!({ - "compilerOptions": { - "jsx": "react-jsx", - "jsxImportSource": "http://localhost:4545/jsx", - }, - }) - .to_string(), - ); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let mut diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.tsx").unwrap(), - "languageId": "typescriptreact", - "version": 1, - "text": " - export function Foo() { - return <div></div>; - } - ", - }, - })); - // The caching is done on an asynchronous task spawned after init, so there's - // a chance it wasn't done in time and we need to wait for another batch of - // diagnostics. - while !diagnostics.all().is_empty() { - std::thread::sleep(std::time::Duration::from_millis(50)); - // The post-cache diagnostics update triggers inconsistently on CI for some - // reason. Force it with this notification. - diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.uri().join("file.tsx").unwrap(), - "languageId": "typescriptreact", - "version": 1, - "text": " - export function Foo() { - return <div></div>; - } - ", - }, - })); - } - assert_eq!(diagnostics.all(), vec![]); - client.shutdown(); -} - -#[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 context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - let contents = r#" -Deno.test({ - name: "test a", - async fn(t) { - console.log("test a"); - await t.step("step of test a", () => {}); - } -}); -"#; - temp_dir.write("./test.ts", contents); - temp_dir.write("./deno.jsonc", "{}"); - let specifier = temp_dir.uri().join("test.ts").unwrap(); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - client.did_open(json!({ - "textDocument": { - "uri": specifier, - "languageId": "typescript", - "version": 1, - "text": contents, - } - })); - - let notification = - client.read_notification_with_method::<Value>("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 = ¶ms.tests[0]; - assert_eq!(test.label, "test a"); - assert_eq!( - test.range, - Some(lsp::Range { - start: lsp::Position { - line: 1, - character: 5, - }, - end: lsp::Position { - line: 1, - character: 9, - } - }) - ); - let steps = test.steps.as_ref().unwrap(); - assert_eq!(steps.len(), 1); - let step = &steps[0]; - assert_eq!(step.label, "step of test a"); - assert_eq!( - step.range, - Some(lsp::Range { - start: lsp::Position { - line: 5, - character: 12, - }, - end: lsp::Position { - line: 5, - character: 16, - } - }) - ); - - let res = client.write_request_with_res_as::<TestRunResponseParams>( - "deno/testRun", - json!({ - "id": 1, - "kind": "run", - }), - ); - 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 notification = - client.read_notification_with_method::<Value>("deno/testRunProgress"); - assert_eq!( - notification, - Some(json!({ - "id": 1, - "message": { - "type": "started", - "test": { - "textDocument": { - "uri": specifier, - }, - "id": id, - }, - } - })) - ); - - let notification = - client.read_notification_with_method::<Value>("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. Occasionally 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 notification = - client.read_notification_with_method::<Value>("deno/testRunProgress"); - assert_eq!( - notification, - Some(json!({ - "id": 1, - "message": { - "type": "started", - "test": { - "textDocument": { - "uri": specifier, - }, - "id": id, - "stepId": step.id, - }, - } - })) - ); - - let notification = - client.read_notification_with_method::<Value>("deno/testRunProgress"); - let mut notification = notification.unwrap(); - let duration = notification - .as_object_mut() - .unwrap() - .get_mut("message") - .unwrap() - .as_object_mut() - .unwrap() - .remove("duration"); - assert!(duration.is_some()); - assert_eq!( - notification, - json!({ - "id": 1, - "message": { - "type": "passed", - "test": { - "textDocument": { - "uri": specifier, - }, - "id": id, - "stepId": step.id, - }, - } - }) - ); - - let notification = - client.read_notification_with_method::<Value>("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 notification = - client.read_notification_with_method::<Value>("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)), - } - - // Regression test for https://github.com/denoland/vscode_deno/issues/899. - temp_dir.write("./test.ts", ""); - client.write_notification( - "textDocument/didChange", - json!({ - "textDocument": { - "uri": temp_dir.uri().join("test.ts").unwrap(), - "version": 2 - }, - "contentChanges": [{ "text": "" }], - }), - ); - - assert_eq!(client.read_diagnostics().all().len(), 0); - - let notification = - client.read_notification_with_method::<Value>("deno/testModuleDelete"); - assert_eq!( - notification, - Some(json!({ - "textDocument": { - "uri": temp_dir.uri().join("test.ts").unwrap() - } - })) - ); - - client.shutdown(); -} - -#[test] -fn lsp_closed_file_find_references() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("./mod.ts", "export const a = 5;"); - temp_dir.write( - "./mod.test.ts", - "import { a } from './mod.ts'; console.log(a);", - ); - let temp_dir_url = temp_dir.uri(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir_url.join("mod.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": r#"export const a = 5;"# - } - })); - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": temp_dir_url.join("mod.ts").unwrap(), - }, - "position": { "line": 0, "character": 13 }, - "context": { - "includeDeclaration": false - } - }), - ); - - assert_eq!( - res, - json!([{ - "uri": temp_dir_url.join("mod.test.ts").unwrap(), - "range": { - "start": { "line": 0, "character": 9 }, - "end": { "line": 0, "character": 10 } - } - }, { - "uri": temp_dir_url.join("mod.test.ts").unwrap(), - "range": { - "start": { "line": 0, "character": 42 }, - "end": { "line": 0, "character": 43 } - } - }]) - ); - - client.shutdown(); -} - -#[test] -fn lsp_closed_file_find_references_low_document_pre_load() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.create_dir_all("sub_dir"); - temp_dir.write("./other_file.ts", "export const b = 5;"); - temp_dir.write("./sub_dir/mod.ts", "export const a = 5;"); - temp_dir.write( - "./sub_dir/mod.test.ts", - "import { a } from './mod.ts'; console.log(a);", - ); - let temp_dir_url = temp_dir.uri(); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_preload_limit(1); - }); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir_url.join("sub_dir/mod.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": r#"export const a = 5;"# - } - })); - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": temp_dir_url.join("sub_dir/mod.ts").unwrap(), - }, - "position": { "line": 0, "character": 13 }, - "context": { - "includeDeclaration": false - } - }), - ); - - // won't have results because the document won't be pre-loaded - assert_eq!(res, json!([])); - - client.shutdown(); -} - -#[test] -fn lsp_closed_file_find_references_excluded_path() { - // we exclude any files or folders in the "exclude" part of - // the config file from being pre-loaded - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.create_dir_all("sub_dir"); - temp_dir.create_dir_all("other_dir/sub_dir"); - temp_dir.write("./sub_dir/mod.ts", "export const a = 5;"); - temp_dir.write( - "./sub_dir/mod.test.ts", - "import { a } from './mod.ts'; console.log(a);", - ); - temp_dir.write( - "./other_dir/sub_dir/mod.test.ts", - "import { a } from '../../sub_dir/mod.ts'; console.log(a);", - ); - temp_dir.write( - "deno.json", - r#"{ - "exclude": [ - "./sub_dir/mod.test.ts", - "./other_dir/sub_dir", - ] -}"#, - ); - let temp_dir_url = temp_dir.uri(); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir_url.join("sub_dir/mod.ts").unwrap(), - "languageId": "typescript", - "version": 1, - "text": r#"export const a = 5;"# - } - })); - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": temp_dir_url.join("sub_dir/mod.ts").unwrap(), - }, - "position": { "line": 0, "character": 13 }, - "context": { - "includeDeclaration": false - } - }), - ); - - // won't have results because the documents won't be pre-loaded - assert_eq!(res, json!([])); - - client.shutdown(); -} - -#[test] -fn lsp_data_urls_with_jsx_compiler_option() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.json", - r#"{ "compilerOptions": { "jsx": "react-jsx" } }"#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - let uri = Url::from_file_path(temp_dir.path().join("main.ts")).unwrap(); - - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": "import a from \"data:application/typescript,export default 5;\";\na;" - } - })).all(); - - assert_eq!(diagnostics.len(), 0); - - let res: Value = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": uri - }, - "position": { "line": 1, "character": 1 }, - "context": { - "includeDeclaration": false - } - }), - ); - - assert_eq!( - res, - json!([{ - "uri": uri, - "range": { - "start": { "line": 0, "character": 7 }, - "end": { "line": 0, "character": 8 } - } - }, { - "uri": uri, - "range": { - "start": { "line": 1, "character": 0 }, - "end": { "line": 1, "character": 1 } - } - }, { - "uri": "deno:/ed0224c51f7e2a845dfc0941ed6959675e5e3e3d2a39b127f0ff569c1ffda8d8/data_url.ts", - "range": { - "start": { "line": 0, "character": 7 }, - "end": {"line": 0, "character": 14 }, - }, - }]) - ); - - client.shutdown(); -} - -#[test] -fn lsp_node_modules_dir() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - - // having a package.json should have no effect on whether - // a node_modules dir is created - temp_dir.write("package.json", "{}"); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let file_uri = temp_dir.uri().join("file.ts").unwrap(); - client.did_open(json!({ - "textDocument": { - "uri": file_uri, - "languageId": "typescript", - "version": 1, - "text": "import chalk from 'npm:chalk';\nimport path from 'node:path';\n\nconsole.log(chalk.green(path.join('a', 'b')));", - } - })); - let cache = |client: &mut LspClient| { - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [["npm:chalk", "npm:@types/node"], file_uri], - }), - ); - }; - - cache(&mut client); - - assert!(!temp_dir.path().join("node_modules").exists()); - - temp_dir.write( - temp_dir.path().join("deno.json"), - "{ \"nodeModulesDir\": true, \"lock\": false }\n", - ); - let refresh_config = |client: &mut LspClient| { - client.change_configuration(json!({ "deno": { - "enable": true, - "config": "./deno.json", - "codeLens": { - "implementations": true, - "references": true, - }, - "importMap": null, - "lint": false, - "suggest": { - "autoImports": true, - "completeFunctionCalls": false, - "names": true, - "paths": true, - "imports": {}, - }, - "unstable": false, - } })); - }; - refresh_config(&mut client); - - let diagnostics = client.read_diagnostics(); - assert_eq!(diagnostics.all().len(), 2, "{:#?}", diagnostics); // not cached - - cache(&mut client); - - assert!(temp_dir.path().join("node_modules/chalk").exists()); - assert!(temp_dir.path().join("node_modules/@types/node").exists()); - assert!(!temp_dir.path().join("deno.lock").exists()); - - // now add a lockfile and cache - temp_dir.write( - temp_dir.path().join("deno.json"), - "{ \"nodeModulesDir\": true }\n", - ); - refresh_config(&mut client); - cache(&mut client); - - let diagnostics = client.read_diagnostics(); - assert_eq!(diagnostics.all().len(), 0, "{:#?}", diagnostics); - - assert!(temp_dir.path().join("deno.lock").exists()); - - // the declaration should be found in the node_modules directory - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": file_uri, - }, - "position": { "line": 0, "character": 7 }, // chalk - "context": { - "includeDeclaration": false - } - }), - ); - - // ensure that it's using the node_modules directory - let references = res.as_array().unwrap(); - assert_eq!(references.len(), 2, "references: {:#?}", references); - let uri = references[1] - .as_object() - .unwrap() - .get("uri") - .unwrap() - .as_str() - .unwrap(); - // canonicalize for mac - let path = temp_dir.path().join("node_modules").canonicalize(); - assert_starts_with!( - uri, - ModuleSpecifier::from_file_path(&path).unwrap().as_str() - ); - - client.shutdown(); -} - -#[test] -fn lsp_vendor_dir() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let local_file_uri = temp_dir.uri().join("file.ts").unwrap(); - client.did_open(json!({ - "textDocument": { - "uri": local_file_uri, - "languageId": "typescript", - "version": 1, - "text": "import { returnsHi } from 'http://localhost:4545/subdir/mod1.ts';\nconst test: string = returnsHi();\nconsole.log(test);", - } - })); - let cache = |client: &mut LspClient| { - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [["http://localhost:4545/subdir/mod1.ts"], local_file_uri], - }), - ); - }; - - cache(&mut client); - - assert!(!temp_dir.path().join("vendor").exists()); - - temp_dir.write( - temp_dir.path().join("deno.json"), - "{ \"vendor\": true, \"lock\": false }\n", - ); - let refresh_config = |client: &mut LspClient| { - client.change_configuration(json!({ "deno": { - "enable": true, - "config": "./deno.json", - "codeLens": { - "implementations": true, - "references": true, - }, - "importMap": null, - "lint": false, - "suggest": { - "autoImports": true, - "completeFunctionCalls": false, - "names": true, - "paths": true, - "imports": {}, - }, - "unstable": false, - } })); - }; - refresh_config(&mut client); - - let diagnostics = client.read_diagnostics(); - assert_eq!(diagnostics.all().len(), 0, "{:#?}", diagnostics); // cached - - // no caching necessary because it was already cached. It should exist now - assert!(temp_dir - .path() - .join("vendor/http_localhost_4545/subdir/mod1.ts") - .exists()); - - // the declaration should be found in the vendor directory - let res = client.write_request( - "textDocument/references", - json!({ - "textDocument": { - "uri": local_file_uri, - }, - "position": { "line": 0, "character": 9 }, // returnsHi - "context": { - "includeDeclaration": false - } - }), - ); - - // ensure that it's using the vendor directory - let references = res.as_array().unwrap(); - assert_eq!(references.len(), 2, "references: {:#?}", references); - let uri = references[1] - .as_object() - .unwrap() - .get("uri") - .unwrap() - .as_str() - .unwrap(); - let file_path = temp_dir - .path() - .join("vendor/http_localhost_4545/subdir/mod1.ts"); - let remote_file_uri = file_path.uri_file(); - assert_eq!(uri, remote_file_uri.as_str()); - - let file_text = file_path.read_to_string(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": remote_file_uri, - "languageId": "typescript", - "version": 1, - "text": file_text, - } - })); - assert_eq!(diagnostics.all(), Vec::new()); - - client.write_notification( - "textDocument/didChange", - json!({ - "textDocument": { - "uri": remote_file_uri, - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 17, "character": 0 }, - }, - "text": "export function returnsHi(): number { return new Date(); }" - } - ] - }), - ); - - let diagnostics = client.read_diagnostics(); - - assert_eq!( - json!( - diagnostics - .messages_with_file_and_source(remote_file_uri.as_str(), "deno-ts") - .diagnostics - ), - json!([ - { - "range": { - "start": { "line": 0, "character": 38 }, - "end": { "line": 0, "character": 44 } - }, - "severity": 1, - "code": 2322, - "source": "deno-ts", - "message": "Type 'Date' is not assignable to type 'number'." - } - ]), - ); - - assert_eq!( - json!( - diagnostics - .messages_with_file_and_source(local_file_uri.as_str(), "deno-ts") - .diagnostics - ), - json!([ - { - "range": { - "start": { "line": 1, "character": 6 }, - "end": { "line": 1, "character": 10 } - }, - "severity": 1, - "code": 2322, - "source": "deno-ts", - "message": "Type 'number' is not assignable to type 'string'." - } - ]), - ); - assert_eq!(diagnostics.all().len(), 2); - - // now try doing a relative import into the vendor directory - client.write_notification( - "textDocument/didChange", - json!({ - "textDocument": { - "uri": local_file_uri, - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 2, "character": 0 }, - }, - "text": "import { returnsHi } from './vendor/subdir/mod1.ts';\nconst test: string = returnsHi();\nconsole.log(test);" - } - ] - }), - ); - - let diagnostics = client.read_diagnostics(); - - assert_eq!( - json!( - diagnostics - .messages_with_file_and_source(local_file_uri.as_str(), "deno") - .diagnostics - ), - json!([ - { - "range": { - "start": { "line": 0, "character": 26 }, - "end": { "line": 0, "character": 51 } - }, - "severity": 1, - "code": "resolver-error", - "source": "deno", - "message": "Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring." - } - ]), - ); - - client.shutdown(); -} - -#[test] -fn lsp_import_unstable_bare_node_builtins_auto_discovered() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - - let contents = r#"import path from "path";"#; - temp_dir.write("main.ts", contents); - temp_dir.write("deno.json", r#"{ "unstable": ["bare-node-builtins"] }"#); - let main_script = temp_dir.uri().join("main.ts").unwrap(); - - let mut client = context.new_lsp_command().capture_stderr().build(); - client.initialize_default(); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": main_script, - "languageId": "typescript", - "version": 1, - "text": contents, - } - })); - - let diagnostics = diagnostics - .messages_with_file_and_source(main_script.as_ref(), "deno") - .diagnostics - .into_iter() - .filter(|d| { - d.code - == Some(lsp::NumberOrString::String( - "import-node-prefix-missing".to_string(), - )) - }) - .collect::<Vec<_>>(); - - // get the quick fixes - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": main_script - }, - "range": { - "start": { "line": 0, "character": 16 }, - "end": { "line": 0, "character": 18 }, - }, - "context": { - "diagnostics": json!(diagnostics), - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Update specifier to node:path", - "kind": "quickfix", - "diagnostics": [ - { - "range": { - "start": { "line": 0, "character": 17 }, - "end": { "line": 0, "character": 23 } - }, - "severity": 2, - "code": "import-node-prefix-missing", - "source": "deno", - "message": "\"path\" is resolved to \"node:path\". If you want to use a built-in Node module, add a \"node:\" prefix.", - "data": { - "specifier": "path" - }, - } - ], - "edit": { - "changes": { - main_script: [ - { - "range": { - "start": { "line": 0, "character": 17 }, - "end": { "line": 0, "character": 23 } - }, - "newText": "\"node:path\"" - } - ] - } - } - }]) - ); - - client.shutdown(); -} - -#[test] -fn lsp_jupyter_byonm_diagnostics() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir().path(); - temp_dir.join("package.json").write_json(&json!({ - "dependencies": { - "@denotest/esm-basic": "*" - } - })); - temp_dir.join("deno.json").write_json(&json!({ - "unstable": ["byonm"] - })); - context.run_npm("install"); - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - let notebook_specifier = temp_dir.join("notebook.ipynb").uri_file(); - let notebook_specifier = format!( - "{}#abc", - notebook_specifier - .to_string() - .replace("file://", "deno-notebook-cell:") - ); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": notebook_specifier, - "languageId": "typescript", - "version": 1, - "text": "import { getValue, nonExistent } from '@denotest/esm-basic';\n console.log(getValue, nonExistent);", - }, - })); - assert_eq!( - json!(diagnostics.all_messages()), - json!([ - { - "uri": notebook_specifier, - "diagnostics": [ - { - "range": { - "start": { - "line": 0, - "character": 19, - }, - "end": { - "line": 0, - "character": 30, - }, - }, - "severity": 1, - "code": 2305, - "source": "deno-ts", - "message": "Module '\"@denotest/esm-basic\"' has no exported member 'nonExistent'.", - }, - ], - "version": 1, - }, - ]) - ); - client.shutdown(); -} - -#[test] -fn lsp_sloppy_imports_warn() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - let temp_dir = temp_dir.path(); - temp_dir - .join("deno.json") - .write(r#"{ "unstable": ["sloppy-imports"] }"#); - // should work when exists on the fs and when doesn't - temp_dir.join("a.ts").write("export class A {}"); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_root_uri(temp_dir.uri_dir()); - }); - client.did_open(json!({ - "textDocument": { - "uri": temp_dir.join("b.ts").uri_file(), - "languageId": "typescript", - "version": 1, - "text": "export class B {}", - }, - })); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.join("file.ts").uri_file(), - "languageId": "typescript", - "version": 1, - "text": "import * as a from './a';\nimport * as b from './b.js';\nconsole.log(a)\nconsole.log(b);\n", - }, - })); - assert_eq!( - diagnostics.messages_with_source("deno"), - lsp::PublishDiagnosticsParams { - uri: temp_dir.join("file.ts").uri_file(), - diagnostics: vec![ - lsp::Diagnostic { - range: lsp::Range { - start: lsp::Position { - line: 0, - character: 19 - }, - end: lsp::Position { - line: 0, - character: 24 - } - }, - severity: Some(lsp::DiagnosticSeverity::INFORMATION), - code: Some(lsp::NumberOrString::String("redirect".to_string())), - source: Some("deno".to_string()), - message: format!( - "The import of \"{}\" was redirected to \"{}\".", - temp_dir.join("a").uri_file(), - temp_dir.join("a.ts").uri_file() - ), - data: Some(json!({ - "specifier": temp_dir.join("a").uri_file(), - "redirect": temp_dir.join("a.ts").uri_file() - })), - ..Default::default() - }, - lsp::Diagnostic { - range: lsp::Range { - start: lsp::Position { - line: 1, - character: 19 - }, - end: lsp::Position { - line: 1, - character: 27 - } - }, - severity: Some(lsp::DiagnosticSeverity::INFORMATION), - code: Some(lsp::NumberOrString::String("redirect".to_string())), - source: Some("deno".to_string()), - message: format!( - "The import of \"{}\" was redirected to \"{}\".", - temp_dir.join("b.js").uri_file(), - temp_dir.join("b.ts").uri_file() - ), - data: Some(json!({ - "specifier": temp_dir.join("b.js").uri_file(), - "redirect": temp_dir.join("b.ts").uri_file() - })), - ..Default::default() - } - ], - version: Some(1), - } - ); - - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": temp_dir.join("file.ts").uri_file() - }, - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "severity": 3, - "code": "redirect", - "source": "deno", - "message": format!( - "The import of \"{}\" was redirected to \"{}\".", - temp_dir.join("a").uri_file(), - temp_dir.join("a.ts").uri_file() - ), - "data": { - "specifier": temp_dir.join("a").uri_file(), - "redirect": temp_dir.join("a.ts").uri_file(), - }, - }], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Update specifier to its redirected specifier.", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "severity": 3, - "code": "redirect", - "source": "deno", - "message": format!( - "The import of \"{}\" was redirected to \"{}\".", - temp_dir.join("a").uri_file(), - temp_dir.join("a.ts").uri_file() - ), - "data": { - "specifier": temp_dir.join("a").uri_file(), - "redirect": temp_dir.join("a.ts").uri_file() - }, - }], - "edit": { - "changes": { - temp_dir.join("file.ts").uri_file(): [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "newText": "\"./a.ts\"" - }] - } - } - }]) - ); - - client.shutdown(); -} - -#[test] -fn sloppy_imports_not_enabled() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - let temp_dir = temp_dir.path(); - temp_dir.join("deno.json").write(r#"{}"#); - // The enhanced, more helpful error message is only available - // when the file exists on the file system at the moment because - // it's a little more complicated to hook it up otherwise. - temp_dir.join("a.ts").write("export class A {}"); - let mut client = context.new_lsp_command().build(); - client.initialize(|builder| { - builder.set_root_uri(temp_dir.uri_dir()); - }); - let diagnostics = client.did_open(json!({ - "textDocument": { - "uri": temp_dir.join("file.ts").uri_file(), - "languageId": "typescript", - "version": 1, - "text": "import * as a from './a';\nconsole.log(a)\n", - }, - })); - assert_eq!( - diagnostics.messages_with_source("deno"), - lsp::PublishDiagnosticsParams { - uri: temp_dir.join("file.ts").uri_file(), - diagnostics: vec![lsp::Diagnostic { - range: lsp::Range { - start: lsp::Position { - line: 0, - character: 19 - }, - end: lsp::Position { - line: 0, - character: 24 - } - }, - severity: Some(lsp::DiagnosticSeverity::ERROR), - code: Some(lsp::NumberOrString::String("no-local".to_string())), - source: Some("deno".to_string()), - message: format!( - "Unable to load a local module: {}\nMaybe add a '.ts' extension.", - temp_dir.join("a").uri_file(), - ), - data: Some(json!({ - "specifier": temp_dir.join("a").uri_file(), - "to": temp_dir.join("a.ts").uri_file(), - "message": "Add a '.ts' extension.", - })), - ..Default::default() - }], - version: Some(1), - } - ); - let res = client.write_request( - "textDocument/codeAction", - json!({ - "textDocument": { - "uri": temp_dir.join("file.ts").uri_file() - }, - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "context": { - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "severity": 3, - "code": "no-local", - "source": "deno", - "message": format!( - "Unable to load a local module: {}\nMaybe add a '.ts' extension.", - temp_dir.join("a").uri_file(), - ), - "data": { - "specifier": temp_dir.join("a").uri_file(), - "to": temp_dir.join("a.ts").uri_file(), - "message": "Add a '.ts' extension.", - }, - }], - "only": ["quickfix"] - } - }), - ); - assert_eq!( - res, - json!([{ - "title": "Add a '.ts' extension.", - "kind": "quickfix", - "diagnostics": [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "severity": 3, - "code": "no-local", - "source": "deno", - "message": format!( - "Unable to load a local module: {}\nMaybe add a '.ts' extension.", - temp_dir.join("a").uri_file(), - ), - "data": { - "specifier": temp_dir.join("a").uri_file(), - "to": temp_dir.join("a.ts").uri_file(), - "message": "Add a '.ts' extension.", - }, - }], - "edit": { - "changes": { - temp_dir.join("file.ts").uri_file(): [{ - "range": { - "start": { "line": 0, "character": 19 }, - "end": { "line": 0, "character": 24 } - }, - "newText": "\"./a.ts\"" - }] - } - } - }]) - ); - client.shutdown(); -} - -#[test] -fn decorators_tc39() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("deno.json", r#"{}"#); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - let uri = Url::from_file_path(temp_dir.path().join("main.ts")).unwrap(); - - let diagnostics = client - .did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": r#"// deno-lint-ignore no-explicit-any -function logged(value: any, { kind, name }: { kind: string; name: string }) { - if (kind === "method") { - return function (...args: unknown[]) { - console.log(`starting ${name} with arguments ${args.join(", ")}`); - // @ts-ignore this has implicit any type - const ret = value.call(this, ...args); - console.log(`ending ${name}`); - return ret; - }; - } -} - -class C { - @logged - m(arg: number) { - console.log("C.m", arg); - } -} - -new C().m(1); -"# - } - })) - .all(); - - assert_eq!(diagnostics.len(), 0); - - client.shutdown(); -} - -#[test] -fn decorators_ts() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "deno.json", - r#"{ "compilerOptions": { "experimentalDecorators": true } }"#, - ); - - let mut client = context.new_lsp_command().build(); - client.initialize_default(); - - let uri = Url::from_file_path(temp_dir.path().join("main.ts")).unwrap(); - - let diagnostics = client - .did_open(json!({ - "textDocument": { - "uri": uri, - "languageId": "typescript", - "version": 1, - "text": r#"// deno-lint-ignore-file -function a() { - console.log("@A evaluated"); - return function ( - _target: any, - _propertyKey: string, - descriptor: PropertyDescriptor, - ) { - console.log("@A called"); - const fn = descriptor.value; - descriptor.value = function () { - console.log("fn() called from @A"); - fn(); - }; - }; -} - -class C { - @a() - static test() { - console.log("C.test() called"); - } -} - -C.test(); -"# - } - })) - .all(); - - assert_eq!(diagnostics.len(), 0); - - client.shutdown(); -} diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs deleted file mode 100644 index 19796f245..000000000 --- a/cli/tests/integration/mod.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -#[macro_export] -macro_rules! itest( -($name:ident {$( $key:ident: $value:expr,)*}) => { - #[test] - fn $name() { - let test = test_util::CheckOutputIntegrationTest { - $( - $key: $value, - )* - .. Default::default() - }; - let output = test.output(); - output.assert_exit_code(test.exit_code); - if !test.output.is_empty() { - assert!(test.output_str.is_none()); - output.assert_matches_file(test.output); - } else { - output.assert_matches_text(test.output_str.unwrap_or("")); - } - } -} -); - -#[macro_export] -macro_rules! itest_flaky( -($name:ident {$( $key:ident: $value:expr,)*}) => { - #[flaky_test::flaky_test] - fn $name() { - let test = test_util::CheckOutputIntegrationTest { - $( - $key: $value, - )* - .. Default::default() - }; - let output = test.output(); - output.assert_exit_code(test.exit_code); - if !test.output.is_empty() { - assert!(test.output_str.is_none()); - output.assert_matches_file(test.output); - } else { - output.assert_matches_text(test.output_str.unwrap_or("")); - } - } -} -); - -#[macro_export] -macro_rules! context( -({$( $key:ident: $value:expr,)*}) => { - test_util::TestContext::create(test_util::TestContextOptions { - $( - $key: $value, - )* - .. Default::default() - }) -} -); - -#[macro_export] -macro_rules! itest_steps( -($name:ident {$( $key:ident: $value:expr,)*}) => { - #[test] - fn $name() { - (test_util::CheckOutputIntegrationTestSteps { - $( - $key: $value, - )* - .. Default::default() - }).run() - } -} -); - -#[macro_export] -macro_rules! command_step( -({$( $key:ident: $value:expr,)*}) => { - test_util::CheckOutputIntegrationTestCommandStep { - $( - $key: $value, - )* - .. Default::default() - } -} -); - -// 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 = "jsr_tests.rs"] -mod jsr; -#[path = "jupyter_tests.rs"] -mod jupyter; -#[path = "lint_tests.rs"] -mod lint; -#[path = "lsp_tests.rs"] -mod lsp; -#[path = "node_compat_tests.rs"] -mod node_compat_tests; -#[path = "node_unit_tests.rs"] -mod node_unit_tests; -#[path = "npm_tests.rs"] -mod npm; -#[path = "publish_tests.rs"] -mod publish; - -#[path = "repl_tests.rs"] -mod repl; -#[path = "run_tests.rs"] -mod run; -#[path = "shared_library_tests.rs"] -mod shared_library_tests; -#[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/node_compat_tests.rs b/cli/tests/integration/node_compat_tests.rs deleted file mode 100644 index 0ed84594b..000000000 --- a/cli/tests/integration/node_compat_tests.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use test_util as util; -use util::env_vars_for_npm_tests; - -#[test] -fn node_compat_tests() { - let mut deno = util::deno_cmd() - .current_dir(util::root_path()) - .arg("test") - .arg("--config") - .arg("cli/tests/config/deno.json") - .arg("--no-lock") - .arg("--unstable") - .arg("-A") - .arg(util::tests_path().join("node_compat")) - .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()); -} - -itest!(node_test_module { - args: "test node/test.js", - output: "node/test.out", - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); diff --git a/cli/tests/integration/node_unit_tests.rs b/cli/tests/integration/node_unit_tests.rs deleted file mode 100644 index 5afaf48c5..000000000 --- a/cli/tests/integration/node_unit_tests.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use std::io::BufRead; -use std::io::BufReader; -use std::time::Duration; -use std::time::Instant; -use test_util as util; -use util::env_vars_for_npm_tests; - -util::unit_test_factory!( - node_unit_test, - "../tests/unit_node", - "**/*_test.ts", - [ - _fs_access_test = _fs / _fs_access_test, - _fs_appendFile_test = _fs / _fs_appendFile_test, - _fs_chmod_test = _fs / _fs_chmod_test, - _fs_chown_test = _fs / _fs_chown_test, - _fs_close_test = _fs / _fs_close_test, - _fs_copy_test = _fs / _fs_copy_test, - _fs_dir_test = _fs / _fs_dir_test, - _fs_dirent_test = _fs / _fs_dirent_test, - _fs_open_test = _fs / _fs_open_test, - _fs_read_test = _fs / _fs_read_test, - _fs_exists_test = _fs / _fs_exists_test, - _fs_fdatasync_test = _fs / _fs_fdatasync_test, - _fs_fstat_test = _fs / _fs_fstat_test, - _fs_fsync_test = _fs / _fs_fsync_test, - _fs_ftruncate_test = _fs / _fs_ftruncate_test, - _fs_futimes_test = _fs / _fs_futimes_test, - _fs_handle_test = _fs / _fs_handle_test, - _fs_link_test = _fs / _fs_link_test, - _fs_lstat_test = _fs / _fs_lstat_test, - _fs_mkdir_test = _fs / _fs_mkdir_test, - _fs_mkdtemp_test = _fs / _fs_mkdtemp_test, - _fs_opendir_test = _fs / _fs_opendir_test, - _fs_readFile_test = _fs / _fs_readFile_test, - _fs_readdir_test = _fs / _fs_readdir_test, - _fs_readlink_test = _fs / _fs_readlink_test, - _fs_realpath_test = _fs / _fs_realpath_test, - _fs_rename_test = _fs / _fs_rename_test, - _fs_rm_test = _fs / _fs_rm_test, - _fs_rmdir_test = _fs / _fs_rmdir_test, - _fs_stat_test = _fs / _fs_stat_test, - _fs_symlink_test = _fs / _fs_symlink_test, - _fs_truncate_test = _fs / _fs_truncate_test, - _fs_unlink_test = _fs / _fs_unlink_test, - _fs_utimes_test = _fs / _fs_utimes_test, - _fs_watch_test = _fs / _fs_watch_test, - _fs_writeFile_test = _fs / _fs_writeFile_test, - _fs_write_test = _fs / _fs_write_test, - async_hooks_test, - assertion_error_test, - buffer_test, - child_process_test, - console_test, - crypto_cipher_test = crypto / crypto_cipher_test, - crypto_cipher_gcm_test = crypto / crypto_cipher_gcm_test, - crypto_hash_test = crypto / crypto_hash_test, - crypto_key_test = crypto / crypto_key_test, - crypto_sign_test = crypto / crypto_sign_test, - events_test, - dgram_test, - fs_test, - http_test, - http2_test, - _randomBytes_test = internal / _randomBytes_test, - _randomFill_test = internal / _randomFill_test, - _randomInt_test = internal / _randomInt_test, - pbkdf2_test = internal / pbkdf2_test, - scrypt_test = internal / scrypt_test, - module_test, - net_test, - os_test, - path_test, - perf_hooks_test, - process_test, - querystring_test, - readline_test, - repl_test, - stream_test, - string_decoder_test, - timers_test, - tls_test, - tty_test, - util_test, - v8_test, - vm_test, - worker_threads_test, - zlib_test - ] -); - -fn node_unit_test(test: String) { - let _g = util::http_server(); - - let mut deno = util::deno_cmd() - .current_dir(util::root_path()) - .arg("test") - .arg("--config") - .arg("cli/tests/config/deno.json") - .arg("--no-lock") - .arg("--unstable") - // TODO(kt3k): This option is required to pass tls_test.ts, - // but this shouldn't be necessary. tls.connect currently doesn't - // pass hostname option correctly and it causes cert errors. - .arg("--unsafely-ignore-certificate-errors") - .arg("-A"); - // Parallel tests for crypto - if test.starts_with("crypto/") { - deno = deno.arg("--parallel"); - } - let mut deno = deno - .arg( - util::tests_path() - .join("unit_node") - .join(format!("{test}.ts")), - ) - .envs(env_vars_for_npm_tests()) - .piped_output() - .spawn() - .expect("failed to spawn script"); - - let now = Instant::now(); - let stdout = deno.stdout.take().unwrap(); - let test_name = test.clone(); - let stdout = std::thread::spawn(move || { - let reader = BufReader::new(stdout); - for line in reader.lines() { - if let Ok(line) = line { - println!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); - } else { - break; - } - } - }); - - let now = Instant::now(); - let stderr = deno.stderr.take().unwrap(); - let test_name = test.clone(); - let stderr = std::thread::spawn(move || { - let reader = BufReader::new(stderr); - for line in reader.lines() { - if let Ok(line) = line { - eprintln!("[{test_name} {:0>6.2}] {line}", now.elapsed().as_secs_f32()); - } else { - break; - } - } - }); - - const PER_TEST_TIMEOUT: Duration = Duration::from_secs(5 * 60); - - let now = Instant::now(); - let status = loop { - if now.elapsed() > PER_TEST_TIMEOUT { - // Last-ditch kill - _ = deno.kill(); - panic!("Test {test} failed to complete in time"); - } - if let Some(status) = deno - .try_wait() - .expect("failed to wait for the child process") - { - break status; - } - std::thread::sleep(Duration::from_millis(100)); - }; - - #[cfg(unix)] - assert_eq!( - std::os::unix::process::ExitStatusExt::signal(&status), - None, - "Deno should not have died with a signal" - ); - assert_eq!(Some(0), status.code(), "Deno should have exited cleanly"); - - stdout.join().unwrap(); - stderr.join().unwrap(); - - assert!(status.success()); -} - -// Regression test for https://github.com/denoland/deno/issues/16928 -itest!(unhandled_rejection_web { - args: "run -A node/unhandled_rejection_web.ts", - output: "node/unhandled_rejection_web.ts.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -// Ensure that Web `onunhandledrejection` is fired before -// Node's `process.on('unhandledRejection')`. -itest!(unhandled_rejection_web_process { - args: "run -A node/unhandled_rejection_web_process.ts", - output: "node/unhandled_rejection_web_process.ts.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -// Ensure that Web `onrejectionhandled` is fired before -// Node's `process.on('rejectionHandled')`. -itest!(rejection_handled_web_process { - args: "run -A --quiet node/rejection_handled_web_process.ts", - output: "node/rejection_handled_web_process.ts.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); diff --git a/cli/tests/integration/npm_tests.rs b/cli/tests/integration/npm_tests.rs deleted file mode 100644 index a63253260..000000000 --- a/cli/tests/integration/npm_tests.rs +++ /dev/null @@ -1,2750 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::serde_json; -use deno_core::serde_json::json; -use deno_core::serde_json::Value; -use pretty_assertions::assert_eq; -use test_util as util; -use util::assert_contains; -use util::env_vars_for_npm_tests; -use util::http_server; -use util::TestContextBuilder; - -// NOTE: See how to make test npm packages at ./testdata/npm/README.md - -itest!(es_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!(es_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!(es_module_deno_test { - args: "test --allow-read --allow-env 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 --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!(cjs_invalid_name_exports { - args: "run --allow-read --quiet npm/cjs-invalid-name-exports/main.ts", - output: "npm/cjs-invalid-name-exports/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(cjs_require_esm_error { - args: "run --allow-read --quiet npm/cjs_require_esm_error/main.ts", - output: "npm/cjs_require_esm_error/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(cjs_require_esm_mjs_error { - args: "run --allow-read --quiet npm/cjs_require_esm_mjs_error/main.ts", - output: "npm/cjs_require_esm_mjs_error/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(require_esm_error { - args: "run --allow-read --quiet node/require_esm_error/main.ts", - output: "node/require_esm_error/main.out", - exit_code: 1, -}); - -itest!(dynamic_import_deno_ts_from_npm { - args: "run --allow-read --quiet npm/dynamic_import_deno_ts_from_npm/main.ts", - output: "npm/dynamic_import_deno_ts_from_npm/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -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 --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_node_modules.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 --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 --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, -}); - -itest!(local_dir_resolves_symlinks { - args: "run -A index.js", - output: "npm/local_dir_resolves_symlinks/index.out", - exit_code: 0, - envs: env_vars_for_npm_tests(), - cwd: Some("npm/local_dir_resolves_symlinks/"), - copy_temp_dir: Some("npm/local_dir_resolves_symlinks/"), - http_server: 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!(dynamic_import_invalid_package_name { - args: "run -A --reload npm/dynamic_import_invalid_package_name/main.ts", - output: "npm/dynamic_import_invalid_package_name/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_integrity_failure { - 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 -A npm/remote_npm_specifier/main.ts", - output: "npm/remote_npm_specifier/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 0, -}); - -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!(node_modules_deno_node_modules { - args: "run --quiet npm/node_modules_deno_node_modules/main.ts", - output: "npm/node_modules_deno_node_modules/main.out", - copy_temp_dir: Some("npm/node_modules_deno_node_modules/"), - exit_code: 0, - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(node_modules_deno_node_modules_local { - args: - "run --quiet --node-modules-dir npm/node_modules_deno_node_modules/main.ts", - output: "npm/node_modules_deno_node_modules/main.out", - copy_temp_dir: Some("npm/node_modules_deno_node_modules/"), - exit_code: 0, - 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!(nonexistent_file_node_modules_dir { - // there was a bug where the message was different when using a node_modules dir - args: "run -A --quiet --node-modules-dir npm/nonexistent_file/main.js", - output: "npm/nonexistent_file/main.out", - copy_temp_dir: Some("npm/nonexistent_file/"), - 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 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!(types_d_ext { - args: "check --all npm/d_ext/main.ts", - output: "npm/d_ext/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -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, -}); - -itest!(run_existing_npm_package { - args: "run --allow-read --node-modules-dir npm:@denotest/bin", - output: "npm/run_existing_npm_package/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, - temp_cwd: true, - cwd: Some("npm/run_existing_npm_package/"), - copy_temp_dir: Some("npm/run_existing_npm_package/"), -}); - -itest!(run_existing_npm_package_with_subpath { - args: - "run --allow-read --node-modules-dir npm:@denotest/bin/cli-esm dev --help", - output: "npm/run_existing_npm_package_with_subpath/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, - temp_cwd: true, - cwd: Some("npm/run_existing_npm_package_with_subpath/"), - copy_temp_dir: Some("npm/run_existing_npm_package_with_subpath/"), -}); - -#[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()), - 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()) - .piped_output() - .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, "[Function: chalk] createChalk"); - 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()) - .piped_output() - .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()) - .piped_output() - .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, "[Function: chalk] createChalk"); -} - -#[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()) - .piped_output() - .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, "[Function: chalk] createChalk"); - 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()) - .piped_output() - .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, "[Function: chalk] createChalk"); - 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()) - .piped_output() - .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, "[Function: chalk] createChalk"); - 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()) - .piped_output() - .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, "[Function: chalk] createChalk"); - 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()) - .piped_output() - .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, "[Function: chalk] createChalk"); - 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()) - .piped_output() - .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, - "error: npm specifiers were requested; but --no-npm is specified\n at file:///" - ); - 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()) - .piped_output() - .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, "[Function: chalk] createChalk"); - 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()) - .piped_output() - .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, - "error: npm specifiers were requested; but --no-npm is specified\n at file:///" - ); - 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(), - http_server: true, -}); - -itest!(deno_run_cowsay_with_node_modules_dir { - args: "run -A --quiet --node-modules-dir npm:cowsay@1.5.0 Hello", - temp_cwd: true, - output: "npm/deno_run_cowsay.out", - envs: env_vars_for_npm_tests(), - 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(), - 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(), - 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_esm_no_bin_entrypoint { - args: "run -A --quiet npm:@denotest/bin@0.6.0/cli.mjs this is a test", - output: "npm/deno_run_esm.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(deno_run_bin_cjs_no_bin_entrypoint { - args: "run -A --quiet npm:@denotest/bin@0.6.0/cli-cjs.js this is a test", - output: "npm/deno_run_cjs.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(deno_run_bin_special_chars { - args: "run -A --quiet npm:@denotest/special-chars-in-bin-name/\\foo\" this is a test", - output: "npm/deno_run_special_chars_in_bin_name.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, -}); - -#[test] -fn deno_run_bin_lockfile() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("deno.json", "{}"); - let output = context - .new_command() - .args("run -A --quiet npm:@denotest/bin/cli-esm this is a test") - .run(); - output.assert_matches_file("npm/deno_run_esm.out"); - assert!(temp_dir.path().join("deno.lock").exists()); -} - -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!(deno_run_no_bin_entrypoint { - args: "run -A --quiet npm:@denotest/esm-basic", - output: "npm/deno_run_no_bin_entrypoint.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(deno_run_no_bin_entrypoint_non_existent_subpath { - args: "run -A --quiet npm:@denotest/esm-basic/non-existent.js", - output: "npm/deno_run_no_bin_entrypoint_non_existent_subpath.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(directory_import_folder_index_js { - args: "run npm/directory_import/folder_index_js.ts", - output: "npm/directory_import/folder_index_js.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(directory_import_folder_no_index { - args: "run npm/directory_import/folder_no_index.ts", - output: "npm/directory_import/folder_no_index.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_require_main_entry { - args: "run --node-modules-dir -A --quiet $TESTDATA/npm/require_main/main.js", - output: "npm/require_main/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_node_modules.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(®istry_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(®istry_json_path).unwrap(); - if file_text.contains("https://registry.npmjs.org/") { - panic!( - "file {} contained a reference to the npm registry", - registry_json_path - ); - } - } - } - } -} - -itest!(bundle_errors { - args: "bundle --quiet npm/esm/main.js", - output_str: Some("error: npm specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: npm:/chalk@5.0.1\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()) - .piped_output() - .spawn() - .unwrap(); - let output = deno.wait_with_output().unwrap(); - assert!(!output.status.success()); - - let stderr = String::from_utf8(output.stderr).unwrap(); - assert_eq!( - stderr, - concat!( - "error: failed reading lockfile 'deno.lock'\n", - "\n", - "Caused by:\n", - " 0: The lockfile is corrupt. You can recreate it with --lock-write\n", - " 1: Could not find 'cowsay@1.5.0' in the list of packages.\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": "3", - "packages": { - "specifiers": { - "npm:cowsay@1.5.0": "npm:cowsay@1.5.0" - }, - "npm": { - "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" - } - } - } - }, - "remote": {} -} -"#; - 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()) - .piped_output() - .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 context = TestContextBuilder::for_npm().use_temp_cwd().build(); - - let temp_dir = context.temp_dir(); - - // write empty config file - temp_dir.write("deno.json", "{}"); - - // write a lock file with borked integrity - let lock_file_content = r#"{ - "version": "3", - "packages": { - "specifiers": { "npm:@denotest/bin": "npm:@denotest/bin@1.0.0" }, - "npm": { - "@denotest/bin@1.0.0": { - "integrity": "sha512-foobar", - "dependencies": {} - } - } - }, - "remote": {} - }"#; - temp_dir.write("deno.lock", lock_file_content); - - let output = context - .new_command() - .args("run -A npm:@denotest/bin/cli-esm test") - .run(); - output - .assert_matches_text( -r#"Download http://localhost:4545/npm/registry/@denotest/bin -error: Integrity check failed for npm package: "@denotest/bin@1.0.0". Unable to verify that the package -is the same as when the lockfile was generated. - -Actual: sha512-[WILDCARD] -Expected: sha512-foobar - -This could be caused by: - * the lock file may be corrupt - * the source itself may be corrupt - -Use "--lock-write" flag to regenerate the lockfile at "[WILDCARD]deno.lock". -"#) - .assert_exit_code(10); -} - -#[test] -fn peer_deps_with_copied_folders_and_lockfile() { - let context = TestContextBuilder::for_npm() - .use_copy_temp_dir("npm/peer_deps_with_copied_folders") - .cwd("npm/peer_deps_with_copied_folders") - .build(); - - let deno_dir = context.deno_dir(); - let temp_dir = context.temp_dir(); - let temp_dir_sub_path = - temp_dir.path().join("npm/peer_deps_with_copied_folders"); - - // write empty config file - temp_dir.write("npm/peer_deps_with_copied_folders/deno.json", "{}"); - - let output = context.new_command().args("run -A main.ts").run(); - output.assert_exit_code(0); - output.assert_matches_file("npm/peer_deps_with_copied_folders/main.out"); - - assert!(temp_dir_sub_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 output = context.new_command().args("run -A main.ts").run(); - output.assert_exit_code(0); - output.assert_matches_text("1\n2\n"); - - // run with reload - let output = context.new_command().args("run -A --reload main.ts").run(); - output.assert_exit_code(0); - output.assert_matches_file("npm/peer_deps_with_copied_folders/main.out"); - - // now run with local node modules - let output = context - .new_command() - .args("run -A --node-modules-dir main.ts") - .run(); - output.assert_exit_code(0); - output.assert_matches_file( - "npm/peer_deps_with_copied_folders/main_node_modules.out", - ); - - let deno_folder = temp_dir_sub_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 output = context - .new_command() - .args("run -A --node-modules-dir main.ts") - .run(); - output.assert_exit_code(0); - output.assert_matches_text("1\n2\n"); - - // now ensure it works with reloading - let output = context - .new_command() - .args("run -A --reload --node-modules-dir main.ts") - .run(); - output.assert_exit_code(0); - output.assert_matches_file( - "npm/peer_deps_with_copied_folders/main_node_modules_reload.out", - ); - - // now ensure it works with reloading and no lockfile - let output = context - .new_command() - .args("run -A --reload --node-modules-dir --no-lock main.ts") - .run(); - output.assert_exit_code(0); - output.assert_matches_file( - "npm/peer_deps_with_copied_folders/main_node_modules_reload.out", - ); -} - -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, -}); - -itest!(node_modules_import_run { - args: "run --quiet main.ts", - output: "npm/node_modules_import/main.out", - http_server: true, - copy_temp_dir: Some("npm/node_modules_import/"), - cwd: Some("npm/node_modules_import/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, -}); - -itest!(node_modules_import_check { - args: "check --quiet main.ts", - output: "npm/node_modules_import/main_check.out", - envs: env_vars_for_npm_tests(), - http_server: true, - cwd: Some("npm/node_modules_import/"), - copy_temp_dir: Some("npm/node_modules_import/"), - exit_code: 1, -}); - -itest!(non_existent_dep { - args: "cache npm:@denotest/non-existent-dep", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, - output_str: Some(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/non-existent-dep\n", - "Download http://localhost:4545/npm/registry/@denotest/non-existent\n", - "[UNORDERED_END]\n", - "error: npm package '@denotest/non-existent' does not exist.\n" - )), -}); - -itest!(non_existent_dep_version { - args: "cache npm:@denotest/non-existent-dep-version", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, - output_str: Some(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/non-existent-dep-version\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "[UNORDERED_END]\n", - // does two downloads because when failing once it max tries to - // get the latest version a second time - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/non-existent-dep-version\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "[UNORDERED_END]\n", - "error: Could not find npm package '@denotest/esm-basic' matching '=99.99.99'.\n" - )), -}); - -#[test] -fn reload_info_not_found_cache_but_exists_remote() { - fn remove_version(registry_json: &mut Value, version: &str) { - registry_json - .as_object_mut() - .unwrap() - .get_mut("versions") - .unwrap() - .as_object_mut() - .unwrap() - .remove(version); - } - - fn remove_version_for_package( - deno_dir: &util::TempDir, - package: &str, - version: &str, - ) { - let registry_json_path = - format!("npm/localhost_4545/npm/registry/{}/registry.json", package); - let mut registry_json: Value = - serde_json::from_str(&deno_dir.read_to_string(®istry_json_path)) - .unwrap(); - remove_version(&mut registry_json, version); - // for the purpose of this test, just remove the dist-tag as it might contain this version - registry_json - .as_object_mut() - .unwrap() - .get_mut("dist-tags") - .unwrap() - .as_object_mut() - .unwrap() - .remove("latest"); - deno_dir.write( - ®istry_json_path, - serde_json::to_string(®istry_json).unwrap(), - ); - } - - // This tests that when a local machine doesn't have a version - // specified in a dependency that exists in the npm registry - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let deno_dir = test_context.deno_dir(); - let temp_dir = test_context.temp_dir(); - temp_dir.write( - "main.ts", - "import 'npm:@denotest/esm-import-cjs-default@1.0.0';", - ); - - // cache successfully to the deno_dir - let output = test_context - .new_command() - .args("cache main.ts npm:@denotest/esm-basic@1.0.0") - .run(); - output.assert_matches_text(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-import-cjs-default\n", - "Download http://localhost:4545/npm/registry/@denotest/cjs-default-export\n", - "Download http://localhost:4545/npm/registry/@denotest/cjs-default-export/1.0.0.tgz\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-import-cjs-default/1.0.0.tgz\n", - "[UNORDERED_END]\n", - )); - - // test in dependency - { - // modify the package information in the cache to remove the latest version - remove_version_for_package( - deno_dir, - "@denotest/cjs-default-export", - "1.0.0", - ); - - // should error when `--cache-only` is used now because the version is not in the cache - let output = test_context - .new_command() - .args("run --cached-only main.ts") - .run(); - output.assert_exit_code(1); - output.assert_matches_text("error: Could not find npm package '@denotest/cjs-default-export' matching '^1.0.0'.\n"); - - // now try running without it, it should download the package now - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-import-cjs-default\n", - "Download http://localhost:4545/npm/registry/@denotest/cjs-default-export\n", - "[UNORDERED_END]\n", - "Node esm importing node cjs\n[WILDCARD]", - )); - output.assert_exit_code(0); - } - - // test in npm specifier - { - // now remove the information for the top level package - remove_version_for_package( - deno_dir, - "@denotest/esm-import-cjs-default", - "1.0.0", - ); - - // should error for --cached-only - let output = test_context - .new_command() - .args("run --cached-only main.ts") - .run(); - output.assert_matches_text(concat!( - "error: Could not find npm package '@denotest/esm-import-cjs-default' matching '1.0.0'.\n", - " at file:///[WILDCARD]/main.ts:1:8\n", - )); - output.assert_exit_code(1); - - // now try running, it should work - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-import-cjs-default\n", - "Download http://localhost:4545/npm/registry/@denotest/cjs-default-export\n", - "[UNORDERED_END]\n", - "Node esm importing node cjs\n[WILDCARD]", - )); - output.assert_exit_code(0); - } - - // test matched specifier in package.json - { - // write out a package.json and a new main.ts with a bare specifier - temp_dir.write("main.ts", "import '@denotest/esm-import-cjs-default';"); - temp_dir.write( - "package.json", - r#"{ "dependencies": { "@denotest/esm-import-cjs-default": "1.0.0" }}"#, - ); - - // remove the top level package information again - remove_version_for_package( - deno_dir, - "@denotest/esm-import-cjs-default", - "1.0.0", - ); - - // should error for --cached-only - let output = test_context - .new_command() - .args("run --cached-only main.ts") - .run(); - output.assert_matches_text(concat!( - "error: Could not find npm package '@denotest/esm-import-cjs-default' matching '1.0.0'.\n", - )); - output.assert_exit_code(1); - - // now try running, it should work - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-import-cjs-default\n", - "Download http://localhost:4545/npm/registry/@denotest/cjs-default-export\n", - "[UNORDERED_END]\n", - "[UNORDERED_START]\n", - "Initialize @denotest/cjs-default-export@1.0.0\n", - "Initialize @denotest/esm-import-cjs-default@1.0.0\n", - "[UNORDERED_END]\n", - "Node esm importing node cjs\n[WILDCARD]", - )); - output.assert_exit_code(0); - } - - // temp other dependency in package.json - { - // write out a package.json that has another dependency - temp_dir.write( - "package.json", - r#"{ "dependencies": { "@denotest/esm-import-cjs-default": "1.0.0", "@denotest/esm-basic": "1.0.0" }}"#, - ); - - // remove the dependency's version - remove_version_for_package(deno_dir, "@denotest/esm-basic", "1.0.0"); - - // should error for --cached-only - let output = test_context - .new_command() - .args("run --cached-only main.ts") - .run(); - output.assert_matches_text(concat!( - "error: Could not find npm package '@denotest/esm-basic' matching '1.0.0'.\n", - )); - output.assert_exit_code(1); - - // now try running, it should work and only initialize the new package - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-import-cjs-default\n", - "Download http://localhost:4545/npm/registry/@denotest/cjs-default-export\n", - "[UNORDERED_END]\n", - "Initialize @denotest/esm-basic@1.0.0\n", - "Node esm importing node cjs\n[WILDCARD]", - )); - output.assert_exit_code(0); - } - - // now try using a lockfile - { - // create it - temp_dir.write("deno.json", r#"{}"#); - test_context.new_command().args("cache main.ts").run(); - assert!(temp_dir.path().join("deno.lock").exists()); - - // remove a version found in the lockfile - remove_version_for_package(deno_dir, "@denotest/esm-basic", "1.0.0"); - - // should error for --cached-only - let output = test_context - .new_command() - .args("run --cached-only main.ts") - .run(); - output.assert_matches_text(concat!( - "error: failed reading lockfile '[WILDCARD]deno.lock'\n", - "\n", - "Caused by:\n", - " 0: Could not find '@denotest/esm-basic@1.0.0' specified in the lockfile.\n", - " 1: Could not find version '1.0.0' for npm package '@denotest/esm-basic'.\n", - )); - output.assert_exit_code(1); - - // now try running, it should work and only initialize the new package - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text(concat!( - "[UNORDERED_START]\n", - "Download http://localhost:4545/npm/registry/@denotest/cjs-default-export\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-import-cjs-default\n", - "[UNORDERED_END]\n", - "Node esm importing node cjs\n[WILDCARD]", - )); - output.assert_exit_code(0); - } -} - -#[test] -fn binary_package_with_optional_dependencies() { - let context = TestContextBuilder::for_npm() - .use_copy_temp_dir("npm/binary_package") - .cwd("npm/binary_package") - .build(); - - let temp_dir = context.temp_dir(); - let temp_dir_path = temp_dir.path(); - let project_path = temp_dir_path.join("npm/binary_package"); - - // write empty config file so a lockfile gets created - temp_dir.write("npm/binary_package/deno.json", "{}"); - - // run it twice, with the first time creating the lockfile and the second using it - for i in 0..2 { - if i == 1 { - assert!(project_path.join("deno.lock").exists()); - } - - let output = context - .new_command() - .args("run -A --node-modules-dir main.js") - .run(); - - #[cfg(target_os = "windows")] - { - output.assert_exit_code(0); - output.assert_matches_text( - "[WILDCARD]Hello from binary package on windows[WILDCARD]", - ); - assert!(project_path - .join("node_modules/.deno/@denotest+binary-package-windows@1.0.0") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package-linux@1.0.0") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package-mac@1.0.0") - .exists()); - assert!(project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-windows") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-linux") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-mac") - .exists()); - } - - #[cfg(target_os = "macos")] - { - output.assert_exit_code(0); - output.assert_matches_text( - "[WILDCARD]Hello from binary package on mac[WILDCARD]", - ); - - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package-windows@1.0.0") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package-linux@1.0.0") - .exists()); - assert!(project_path - .join("node_modules/.deno/@denotest+binary-package-mac@1.0.0") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-windows") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-linux") - .exists()); - assert!(project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-mac") - .exists()); - } - - #[cfg(target_os = "linux")] - { - output.assert_exit_code(0); - output.assert_matches_text( - "[WILDCARD]Hello from binary package on linux[WILDCARD]", - ); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package-windows@1.0.0") - .exists()); - assert!(project_path - .join("node_modules/.deno/@denotest+binary-package-linux@1.0.0") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package-mac@1.0.0") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-windows") - .exists()); - assert!(project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-linux") - .exists()); - assert!(!project_path - .join("node_modules/.deno/@denotest+binary-package@1.0.0/node_modules/@denotest/binary-package-mac") - .exists()); - } - } -} - -#[test] -fn node_modules_dir_config_file() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - let node_modules_dir = temp_dir.path().join("node_modules"); - let rm_node_modules = || std::fs::remove_dir_all(&node_modules_dir).unwrap(); - - temp_dir.write("deno.json", r#"{ "nodeModulesDir": true }"#); - temp_dir.write("main.ts", "import 'npm:@denotest/esm-basic';"); - - let deno_cache_cmd = test_context.new_command().args("cache --quiet main.ts"); - deno_cache_cmd.run(); - assert!(node_modules_dir.exists()); - - // now try adding a vendor flag, it should exist - rm_node_modules(); - temp_dir.write("deno.json", r#"{ "vendor": true }"#); - deno_cache_cmd.run(); - assert!(node_modules_dir.exists()); - - rm_node_modules(); - temp_dir.write("deno.json", r#"{ "nodeModulesDir": false }"#); - - deno_cache_cmd.run(); - assert!(!node_modules_dir.exists()); - - temp_dir.write("package.json", r#"{}"#); - deno_cache_cmd.run(); - assert!(!node_modules_dir.exists()); - - test_context - .new_command() - .args("cache --quiet --node-modules-dir main.ts") - .run(); - assert!(node_modules_dir.exists()); - - // should override the `--vendor` flag - rm_node_modules(); - test_context - .new_command() - .args("cache --quiet --node-modules-dir=false --vendor main.ts") - .run(); - assert!(!node_modules_dir.exists()); -} - -#[test] -fn top_level_install_package_json_explicit_opt_in() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - let node_modules_dir = temp_dir.path().join("node_modules"); - let rm_created_files = || { - std::fs::remove_dir_all(&node_modules_dir).unwrap(); - std::fs::remove_file(temp_dir.path().join("deno.lock")).unwrap(); - }; - - // when the node_modules_dir is explicitly opted into, we should always - // ensure a top level package.json install occurs - temp_dir.write("deno.json", "{ \"nodeModulesDir\": true }"); - temp_dir.write( - "package.json", - "{ \"dependencies\": { \"@denotest/esm-basic\": \"1.0\" }}", - ); - - temp_dir.write("main.ts", "console.log(5);"); - let output = test_context.new_command().args("cache main.ts").run(); - output.assert_matches_text( - concat!( - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz\n", - "Initialize @denotest/esm-basic@1.0.0\n", - ) - ); - - rm_created_files(); - let output = test_context - .new_command() - .args_vec(["eval", "console.log(5)"]) - .run(); - output.assert_matches_text(concat!( - "Initialize @denotest/esm-basic@1.0.0\n", - "5\n" - )); - - rm_created_files(); - let output = test_context - .new_command() - .args("run -") - .stdin_text("console.log(5)") - .run(); - output.assert_matches_text(concat!( - "Initialize @denotest/esm-basic@1.0.0\n", - "5\n" - )); - - // now ensure this is cached in the lsp - rm_created_files(); - let mut client = test_context.new_lsp_command().build(); - client.initialize_default(); - let file_uri = temp_dir.uri().join("file.ts").unwrap(); - client.did_open(json!({ - "textDocument": { - "uri": file_uri, - "languageId": "typescript", - "version": 1, - "text": "", - } - })); - client.write_request( - "workspace/executeCommand", - json!({ - "command": "deno.cache", - "arguments": [[], file_uri], - }), - ); - - assert!(node_modules_dir.join("@denotest").exists()); -} - -itest!(reserved_word_exports { - args: "run npm/reserved_word_exports/main.ts", - output: "npm/reserved_word_exports/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(import_json { - args: "run -A --quiet npm/import_json/main.js", - output: "npm/import_json/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(dynamic_import_json { - args: "run -A --quiet npm/import_json/main.js", - output: "npm/import_json/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(check_package_file_dts_dmts_dcts { - args: "check npm/file_dts_dmts_dcts/main.ts", - output: "npm/file_dts_dmts_dcts/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(require_resolve_url_paths { - args: "run -A --quiet --node-modules-dir url_paths.ts", - output: "npm/require_resolve_url/url_paths.out", - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 0, - cwd: Some("npm/require_resolve_url/"), - copy_temp_dir: Some("npm/require_resolve_url/"), -}); - -#[test] -fn byonm_cjs_esm_packages() { - let test_context = TestContextBuilder::for_npm() - .env("DENO_UNSTABLE_BYONM", "1") - .use_temp_cwd() - .build(); - let dir = test_context.temp_dir(); - - test_context.run_npm("init -y"); - test_context.run_npm("install @denotest/esm-basic @denotest/cjs-default-export @denotest/dual-cjs-esm chalk@4 chai@4.3"); - - dir.write( - "main.ts", - r#" -import { getValue, setValue } from "@denotest/esm-basic"; - -setValue(2); -console.log(getValue()); - -import cjsDefault from "@denotest/cjs-default-export"; -console.log(cjsDefault.default()); -console.log(cjsDefault.named()); - -import { getKind } from "@denotest/dual-cjs-esm"; -console.log(getKind()); - - -"#, - ); - let output = test_context.new_command().args("run --check main.ts").run(); - output - .assert_matches_text("Check file:///[WILDCARD]/main.ts\n2\n1\n2\nesm\n"); - - // should not have created the .deno directory - assert!(!dir.path().join("node_modules/.deno").exists()); - - // try chai - dir.write( - "chai.ts", - r#"import { expect } from "chai"; - - const timeout = setTimeout(() => {}, 0); - expect(timeout).to.be.a("number"); - clearTimeout(timeout);"#, - ); - test_context.new_command().args("run chai.ts").run(); - - // try chalk cjs - dir.write( - "chalk.ts", - "import chalk from 'chalk'; console.log(chalk.green('chalk cjs loads'));", - ); - let output = test_context - .new_command() - .args("run --allow-read chalk.ts") - .run(); - output.assert_matches_text("chalk cjs loads\n"); - - // try using an npm specifier for chalk that matches the version we installed - dir.write( - "chalk.ts", - "import chalk from 'npm:chalk@4'; console.log(chalk.green('chalk cjs loads'));", - ); - let output = test_context - .new_command() - .args("run --allow-read chalk.ts") - .run(); - output.assert_matches_text("chalk cjs loads\n"); - - // try with one that doesn't match the package.json - dir.write( - "chalk.ts", - "import chalk from 'npm:chalk@5'; console.log(chalk.green('chalk cjs loads'));", - ); - let output = test_context - .new_command() - .args("run --allow-read chalk.ts") - .run(); - output.assert_matches_text( - r#"error: Could not find a matching package for 'npm:chalk@5' in '[WILDCARD]package.json'. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno. - at file:///[WILDCARD]chalk.ts:1:19 -"#); - output.assert_exit_code(1); -} - -#[test] -fn byonm_import_map() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let dir = test_context.temp_dir(); - dir.write( - "deno.json", - r#"{ - "imports": { - "basic": "npm:@denotest/esm-basic" - }, - "unstable": [ "byonm" ] -}"#, - ); - dir.write( - "package.json", - r#"{ - "name": "my-project", - "version": "1.0.0", - "type": "module", - "dependencies": { - "@denotest/esm-basic": "^1.0" - } -}"#, - ); - test_context.run_npm("install"); - - dir.write( - "main.ts", - r#" -// import map should resolve -import { getValue } from "basic"; -// and resolving via node resolution -import { setValue } from "@denotest/esm-basic"; - -setValue(5); -console.log(getValue()); -"#, - ); - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text("5\n"); - let output = test_context.new_command().args("check main.ts").run(); - output.assert_matches_text("Check file:///[WILDCARD]/main.ts\n"); -} - -#[test] -fn byonm_package_specifier_not_installed_and_invalid_subpath() { - let test_context = TestContextBuilder::for_npm() - .env("DENO_UNSTABLE_BYONM", "1") - .use_temp_cwd() - .build(); - let dir = test_context.temp_dir(); - dir.path().join("package.json").write_json(&json!({ - "dependencies": { - "chalk": "4", - "@denotest/conditional-exports-strict": "1" - } - })); - dir.write( - "main.ts", - "import chalk from 'chalk'; console.log(chalk.green('hi'));", - ); - - // no npm install has been run, so this should give an informative error - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text( - r#"error: Could not resolve "chalk", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `npm install`? - at file:///[WILDCARD]/main.ts:1:19 -"#, - ); - output.assert_exit_code(1); - - // now test for an invalid sub path after doing an npm install - dir.write( - "main.ts", - "import '@denotest/conditional-exports-strict/test';", - ); - - test_context.run_npm("install"); - - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text( - r#"error: [ERR_PACKAGE_PATH_NOT_EXPORTED] Package subpath './test' is not defined by "exports" in '[WILDCARD]' imported from '[WILDCARD]main.ts' - at file:///[WILDCARD]/main.ts:1:8 -"#, - ); - output.assert_exit_code(1); -} - -#[test] -fn byonm_package_npm_specifier_not_installed_and_invalid_subpath() { - let test_context = TestContextBuilder::for_npm() - .env("DENO_UNSTABLE_BYONM", "1") - .use_temp_cwd() - .build(); - let dir = test_context.temp_dir(); - dir.path().join("package.json").write_json(&json!({ - "dependencies": { - "chalk": "4", - "@denotest/conditional-exports-strict": "1" - } - })); - dir.write( - "main.ts", - "import chalk from 'npm:chalk'; console.log(chalk.green('hi'));", - ); - - // no npm install has been run, so this should give an informative error - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text( - r#"error: Could not find '[WILDCARD]package.json'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `npm install`? - at file:///[WILDCARD]/main.ts:1:19 -"#, - ); - output.assert_exit_code(1); - - // now test for an invalid sub path after doing an npm install - dir.write( - "main.ts", - "import 'npm:@denotest/conditional-exports-strict/test';", - ); - - test_context.run_npm("install"); - - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text( - r#"error: Failed resolving package subpath './test' for '[WILDCARD]package.json' - at file:///[WILDCARD]/main.ts:1:8 -"#, - ); - output.assert_exit_code(1); -} - -#[test] -fn byonm_npm_workspaces() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let dir = test_context.temp_dir(); - dir.write("deno.json", r#"{ "unstable": [ "byonm" ] }"#); - - dir.write( - "package.json", - r#"{ - "name": "my-workspace", - "workspaces": [ - "project-a", - "project-b" - ] -} -"#, - ); - - let project_a_dir = dir.path().join("project-a"); - project_a_dir.create_dir_all(); - project_a_dir.join("package.json").write_json(&json!({ - "name": "project-a", - "version": "1.0.0", - "main": "./index.js", - "type": "module", - "dependencies": { - "chai": "^4.2", - "project-b": "^1" - } - })); - project_a_dir.join("index.js").write( - r#" -import { expect } from "chai"; - -const timeout = setTimeout(() => {}, 0); -expect(timeout).to.be.a("number"); -clearTimeout(timeout); - -export function add(a, b) { - return a + b; -} -"#, - ); - project_a_dir - .join("index.d.ts") - .write("export function add(a: number, b: number): number;"); - - let project_b_dir = dir.path().join("project-b"); - project_b_dir.create_dir_all(); - project_b_dir.join("package.json").write_json(&json!({ - "name": "project-b", - "version": "1.0.0", - "type": "module", - "dependencies": { - "@denotest/esm-basic": "^1.0", - } - })); - project_b_dir.join("main.ts").write( - r#" -import { getValue, setValue } from "@denotest/esm-basic"; - -setValue(5); -console.log(getValue()); - -import { add } from "project-a"; -console.log(add(1, 2)); -"#, - ); - - test_context.run_npm("install"); - - let output = test_context - .new_command() - .args("run ./project-b/main.ts") - .run(); - output.assert_matches_text("5\n3\n"); - let output = test_context - .new_command() - .args("check ./project-b/main.ts") - .run(); - output.assert_matches_text("Check file:///[WILDCARD]/project-b/main.ts\n"); - - // Now a file in the main directory should just be able to - // import it via node resolution even though a package.json - // doesn't exist here - dir.write( - "main.ts", - r#" -import { getValue, setValue } from "@denotest/esm-basic"; - -setValue(7); -console.log(getValue()); -"#, - ); - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text("7\n"); - let output = test_context.new_command().args("check main.ts").run(); - output.assert_matches_text("Check file:///[WILDCARD]/main.ts\n"); -} - -#[test] -fn cjs_export_analysis_require_re_export() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let dir = test_context.temp_dir(); - dir.write("deno.json", r#"{ "unstable": [ "byonm" ] }"#); - - dir.write( - "package.json", - r#"{ "name": "test", "packages": { "my-package": "1.0.0" } }"#, - ); - dir.write( - "main.js", - "import { value1, value2 } from 'my-package';\nconsole.log(value1);\nconsole.log(value2)\n", - ); - - let node_modules_dir = dir.path().join("node_modules"); - - // create a package at node_modules/.multipart/name/nested without a package.json - { - let pkg_dir = node_modules_dir - .join(".multipart") - .join("name") - .join("nested"); - pkg_dir.create_dir_all(); - pkg_dir.join("index.js").write("module.exports.value1 = 5;"); - } - // create a package at node_modules/.multipart/other with a package.json - { - let pkg_dir = node_modules_dir.join(".multipart").join("other"); - pkg_dir.create_dir_all(); - pkg_dir.join("index.js").write("module.exports.value2 = 6;"); - } - // create a package at node_modules/my-package that requires them both - { - let pkg_dir = node_modules_dir.join("my-package"); - pkg_dir.create_dir_all(); - pkg_dir.join("package.json").write_json(&json!({ - "name": "my-package", - "version": "1.0.0", - })); - pkg_dir - .join("index.js") - .write("module.exports = { ...require('.multipart/name/nested/index'), ...require('.multipart/other/index.js') }"); - } - - // the cjs export analysis was previously failing, but it should - // resolve these exports similar to require - let output = test_context - .new_command() - .args("run --allow-read main.js") - .run(); - output.assert_matches_text("5\n6\n"); -} - -#[test] -fn cjs_rexport_analysis_json() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let dir = test_context.temp_dir(); - dir.write("deno.json", r#"{ "unstable": [ "byonm" ] }"#); - - dir.write( - "package.json", - r#"{ "name": "test", "packages": { "my-package": "1.0.0" } }"#, - ); - dir.write( - "main.js", - "import data from 'my-package';\nconsole.log(data);\n", - ); - - let node_modules_dir = dir.path().join("node_modules"); - - // create a package that has a json file at index.json and data.json then folder/index.json - { - let pkg_dir = node_modules_dir.join("data-package"); - pkg_dir.create_dir_all(); - pkg_dir.join("package.json").write_json(&json!({ - "name": "data-package", - "version": "1.0.0", - })); - pkg_dir.join("index.json").write(r#"{ "value": 2 }"#); - pkg_dir.join("data.json").write(r#"{ "value": 3 }"#); - let folder = pkg_dir.join("folder"); - folder.create_dir_all(); - folder.join("index.json").write(r#"{ "value": 4 }"#); - } - // create a package at node_modules/my-package that re-exports a json file - { - let pkg_dir = node_modules_dir.join("my-package"); - pkg_dir.create_dir_all(); - pkg_dir.join("package.json").write_json(&json!({ - "name": "my-package", - "version": "1.0.0", - })); - pkg_dir.join("data.json").write(r#"{ "value": 1 }"#); - pkg_dir.join("index.js").write( - "module.exports = { - data1: require('./data'), - data2: require('data-package'), - data3: require('data-package/data'), - data4: require('data-package/folder'), -};", - ); - } - - let output = test_context - .new_command() - .args("run --allow-read main.js") - .run(); - output.assert_matches_text( - "{ - data1: { value: 1 }, - data2: { value: 2 }, - data3: { value: 3 }, - data4: { value: 4 } -} -", - ); -} - -itest!(imports_package_json { - args: "run --node-modules-dir=false npm/imports_package_json/main.js", - output: "npm/imports_package_json/main.out", - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(imports_package_json_import_not_defined { - args: - "run --node-modules-dir=false npm/imports_package_json/import_not_defined.js", - output: "npm/imports_package_json/import_not_defined.out", - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); - -itest!(imports_package_json_sub_path_import_not_defined { - args: - "run --node-modules-dir=false npm/imports_package_json/sub_path_import_not_defined.js", - output: "npm/imports_package_json/sub_path_import_not_defined.out", - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); - -itest!(different_nested_dep_node_modules_dir_false { - args: "run --quiet --node-modules-dir=false npm/different_nested_dep/main.js", - output: "npm/different_nested_dep/main.out", - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(different_nested_dep_node_modules_dir_true { - args: "run --quiet --node-modules-dir=true main.js", - output: "npm/different_nested_dep/main.out", - copy_temp_dir: Some("npm/different_nested_dep/"), - cwd: Some("npm/different_nested_dep/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -#[test] -fn different_nested_dep_byonm() { - let test_context = TestContextBuilder::for_npm() - .use_copy_temp_dir("npm/different_nested_dep") - .cwd("npm/different_nested_dep/") - .build(); - - test_context.run_npm("install"); - - let output = test_context - .new_command() - .args("run --unstable-byonm main.js") - .run(); - output.assert_matches_file("npm/different_nested_dep/main.out"); -} - -#[test] -fn run_cjs_in_node_modules_folder() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = test_context.temp_dir(); - temp_dir.write("package.json", "{}"); - temp_dir.write("deno.json", r#"{ "unstable": ["byonm"] }"#); - let pkg_dir = temp_dir.path().join("node_modules/package"); - pkg_dir.create_dir_all(); - pkg_dir - .join("package.json") - .write(r#"{ "name": "package" }"#); - pkg_dir - .join("main.js") - .write("console.log('hi'); module.exports = 'hi';"); - test_context - .new_command() - .args("run node_modules/package/main.js") - .run() - .assert_matches_text("hi\n"); -} diff --git a/cli/tests/integration/publish_tests.rs b/cli/tests/integration/publish_tests.rs deleted file mode 100644 index 330a7692b..000000000 --- a/cli/tests/integration/publish_tests.rs +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::serde_json::json; -use test_util::assert_contains; -use test_util::assert_not_contains; -use test_util::env_vars_for_jsr_tests; -use test_util::env_vars_for_npm_tests; -use test_util::TestContextBuilder; - -itest!(no_token { - args: "publish", - cwd: Some("publish/missing_deno_json"), - output: "publish/no_token.out", - exit_code: 1, -}); - -itest!(missing_deno_json { - args: "publish --token 'sadfasdf'", - output: "publish/missing_deno_json.out", - cwd: Some("publish/missing_deno_json"), - exit_code: 1, -}); - -itest!(invalid_fast_check { - args: "publish --token 'sadfasdf'", - output: "publish/invalid_fast_check.out", - cwd: Some("publish/invalid_fast_check"), - exit_code: 1, -}); - -itest!(no_zap { - args: "publish --no-zap --token 'sadfasdf'", - output: "publish/no_zap.out", - cwd: Some("publish/invalid_fast_check"), - envs: env_vars_for_jsr_tests(), - http_server: true, - exit_code: 0, -}); - -itest!(invalid_path { - args: "publish --token 'sadfasdf'", - output: "publish/invalid_path.out", - cwd: Some("publish/invalid_path"), - exit_code: 1, -}); - -itest!(symlink { - args: "publish --token 'sadfasdf' --dry-run", - output: "publish/symlink.out", - cwd: Some("publish/symlink"), - exit_code: 0, -}); - -itest!(invalid_import { - args: "publish --token 'sadfasdf' --dry-run", - output: "publish/invalid_import.out", - cwd: Some("publish/invalid_import"), - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); - -#[test] -fn publish_non_exported_files_using_import_map() { - let context = publish_context_builder().build(); - let temp_dir = context.temp_dir().path(); - temp_dir.join("deno.json").write_json(&json!({ - "name": "@foo/bar", - "version": "1.0.0", - "exports": "./mod.ts", - "imports": { - "@denotest/add": "jsr:@denotest/add@1" - } - })); - // file not in the graph - let other_ts = temp_dir.join("_other.ts"); - other_ts - .write("import { add } from '@denotest/add'; console.log(add(1, 3));"); - let mod_ts = temp_dir.join("mod.ts"); - mod_ts.write("import { add } from '@denotest/add'; console.log(add(1, 2));"); - let output = context - .new_command() - .args("publish --log-level=debug --token 'sadfasdf'") - .run(); - let lines = output.combined_output().split('\n').collect::<Vec<_>>(); - assert!(lines - .iter() - .any(|l| l.contains("Unfurling") && l.ends_with("mod.ts"))); - assert!(lines - .iter() - .any(|l| l.contains("Unfurling") && l.ends_with("other.ts"))); -} - -#[test] -fn publish_warning_not_in_graph() { - let context = publish_context_builder().build(); - let temp_dir = context.temp_dir().path(); - temp_dir.join("deno.json").write_json(&json!({ - "name": "@foo/bar", - "version": "1.0.0", - "exports": "./mod.ts", - })); - // file not in the graph that uses a non-analyzable dynamic import (cause a diagnostic) - let other_ts = temp_dir.join("_other.ts"); - other_ts - .write("const nonAnalyzable = './_other.ts'; await import(nonAnalyzable);"); - let mod_ts = temp_dir.join("mod.ts"); - mod_ts.write( - "export function test(a: number, b: number): number { return a + b; }", - ); - context - .new_command() - .args("publish --token 'sadfasdf'") - .run() - .assert_matches_text( - "[WILDCARD]unable to analyze dynamic import[WILDCARD]", - ); -} - -itest!(javascript_missing_decl_file { - args: "publish --token 'sadfasdf'", - output: "publish/javascript_missing_decl_file.out", - cwd: Some("publish/javascript_missing_decl_file"), - envs: env_vars_for_jsr_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(unanalyzable_dynamic_import { - args: "publish --token 'sadfasdf'", - output: "publish/unanalyzable_dynamic_import.out", - cwd: Some("publish/unanalyzable_dynamic_import"), - envs: env_vars_for_jsr_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(javascript_decl_file { - args: "publish --token 'sadfasdf'", - output: "publish/javascript_decl_file.out", - cwd: Some("publish/javascript_decl_file"), - envs: env_vars_for_jsr_tests(), - http_server: true, - exit_code: 0, -}); - -itest!(successful { - args: "publish --token 'sadfasdf'", - output: "publish/successful.out", - cwd: Some("publish/successful"), - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(node_specifier { - args: "publish --token 'sadfasdf'", - output: "publish/node_specifier.out", - cwd: Some("publish/node_specifier"), - envs: env_vars_for_jsr_tests() - .into_iter() - .chain(env_vars_for_npm_tests().into_iter()) - .collect(), - http_server: true, -}); - -itest!(config_file_jsonc { - args: "publish --token 'sadfasdf'", - output: "publish/deno_jsonc.out", - cwd: Some("publish/deno_jsonc"), - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(workspace_all { - args: "publish --token 'sadfasdf'", - output: "publish/workspace.out", - cwd: Some("publish/workspace"), - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(workspace_individual { - args: "publish --token 'sadfasdf'", - output: "publish/workspace_individual.out", - cwd: Some("publish/workspace/bar"), - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -itest!(dry_run { - args: "publish --token 'sadfasdf' --dry-run", - cwd: Some("publish/successful"), - output: "publish/dry_run.out", - envs: env_vars_for_jsr_tests(), - http_server: true, -}); - -#[test] -fn ignores_directories() { - let context = publish_context_builder().build(); - let temp_dir = context.temp_dir().path(); - temp_dir.join("deno.json").write_json(&json!({ - "name": "@foo/bar", - "version": "1.0.0", - "exclude": [ "ignore" ], - "publish": { - "exclude": [ "ignore2" ] - }, - "exports": "./main_included.ts" - })); - - let ignored_dirs = vec![ - temp_dir.join(".git"), - temp_dir.join("node_modules"), - temp_dir.join("ignore"), - temp_dir.join("ignore2"), - ]; - for ignored_dir in ignored_dirs { - ignored_dir.create_dir_all(); - ignored_dir.join("ignored.ts").write(""); - } - - let sub_dir = temp_dir.join("sub_dir"); - sub_dir.create_dir_all(); - sub_dir.join("sub_included.ts").write(""); - - temp_dir.join("main_included.ts").write(""); - - let output = context - .new_command() - .arg("publish") - .arg("--log-level=debug") - .arg("--token") - .arg("sadfasdf") - .run(); - output.assert_exit_code(0); - let output = output.combined_output(); - assert_contains!(output, "sub_included.ts"); - assert_contains!(output, "main_included.ts"); - assert_not_contains!(output, "ignored.ts"); -} - -#[test] -fn includes_directories() { - let context = publish_context_builder().build(); - let temp_dir = context.temp_dir().path(); - temp_dir.join("deno.json").write_json(&json!({ - "name": "@foo/bar", - "version": "1.0.0", - "exports": "./main.ts", - "publish": { - "include": [ "deno.json", "main.ts" ] - } - })); - - temp_dir.join("main.ts").write(""); - temp_dir.join("ignored.ts").write(""); - - let output = context - .new_command() - .arg("publish") - .arg("--log-level=debug") - .arg("--token") - .arg("sadfasdf") - .run(); - output.assert_exit_code(0); - let output = output.combined_output(); - assert_contains!(output, "main.ts"); - assert_not_contains!(output, "ignored.ts"); -} - -fn publish_context_builder() -> TestContextBuilder { - TestContextBuilder::new() - .use_http_server() - .envs(env_vars_for_jsr_tests()) - .use_temp_cwd() -} diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs deleted file mode 100644 index 0e63f1589..000000000 --- a/cli/tests/integration/repl_tests.rs +++ /dev/null @@ -1,1121 +0,0 @@ -// Copyright 2018-2024 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; -use util::TempDir; -use util::TestContext; -use util::TestContextBuilder; - -#[test] -fn pty_multiline() { - util::with_pty(&["repl"], |mut console| { - console.write_line("(\n1 + 2\n)"); - console.expect("3"); - console.write_line("{\nfoo: \"foo\"\n}"); - console.expect("{ foo: \"foo\" }"); - console.write_line("`\nfoo\n`"); - console.expect("\"\\nfoo\\n\""); - console.write_line("`\n\\`\n`"); - console.expect(r#""\n`\n""#); - console.write_line("'{'"); - console.expect(r#""{""#); - console.write_line("'('"); - console.expect(r#""(""#); - console.write_line("'['"); - console.expect(r#""[""#); - console.write_line("/{/"); - console.expect("/{/"); - console.write_line("/\\(/"); - console.expect("/\\(/"); - console.write_line("/\\[/"); - console.expect("/\\[/"); - console.write_line("console.log(\"{test1} abc {test2} def {{test3}}\".match(/{([^{].+?)}/));"); - console.expect("["); - console.expect(" \"{test1}\","); - console.expect(" \"test1\","); - console.expect(" index: 0,"); - console.expect(" input: \"{test1} abc {test2} def {{test3}}\","); - console.expect(" groups: undefined"); - console.expect("]"); - }); -} - -#[test] -fn pty_null() { - util::with_pty(&["repl"], |mut console| { - console.write_line("null"); - console.expect("null"); - }); -} - -#[test] -fn pty_unpaired_braces() { - for right_brace in &[")", "]", "}"] { - util::with_pty(&["repl"], |mut console| { - console.write_line(right_brace); - console.expect("parse error: Expression expected"); - }); - } -} - -#[test] -fn pty_bad_input() { - util::with_pty(&["repl"], |mut console| { - console.write_line("'\\u{1f3b5}'[0]"); - console.expect("Unterminated string literal"); - }); -} - -#[test] -fn pty_syntax_error_input() { - util::with_pty(&["repl"], |mut console| { - console.write_line("('\\u')"); - console.expect("Bad character escape sequence, expected 4 hex characters"); - - console.write_line("'"); - console.expect("Unterminated string constant"); - - console.write_line("[{'a'}];"); - console.expect("Expected a semicolon"); - }); -} - -#[test] -fn pty_complete_symbol() { - util::with_pty(&["repl"], |mut console| { - console.write_line_raw("Symbol.it\t"); - console.expect("Symbol(Symbol.iterator)"); - }); -} - -#[test] -fn pty_complete_declarations() { - util::with_pty(&["repl"], |mut console| { - console.write_line("class MyClass {}"); - console.expect("undefined"); - console.write_line_raw("My\t"); - console.expect("[class MyClass]"); - console.write_line("let myVar = 2 + 3;"); - console.expect("undefined"); - console.write_line_raw("myV\t"); - console.expect("5"); - }); -} - -#[test] -fn pty_complete_primitives() { - util::with_pty(&["repl"], |mut console| { - console.write_line("let func = function test(){}"); - console.expect("undefined"); - console.write_line_raw("func.appl\t"); - console.expect("func.apply"); - console.write_line("let str = ''"); - console.expect("undefined"); - console.write_line_raw("str.leng\t"); - console.expect("str.length"); - console.write_line_raw("false.valueO\t"); - console.expect("false.valueOf"); - console.write_line_raw("5n.valueO\t"); - console.expect("5n.valueOf"); - console.write_line("let num = 5"); - console.expect("undefined"); - console.write_line_raw("num.toStrin\t"); - console.expect("num.toString"); - }); -} - -#[test] -fn pty_complete_expression() { - util::with_pty(&["repl"], |mut console| { - console.write_raw("Deno.\t\t"); - console.expect("Display all"); - console.write_raw("y"); - console.expect_all(&["symlink", "args", "permissions", "exit"]); - }); -} - -#[test] -fn pty_complete_imports() { - let context = TestContextBuilder::default().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.create_dir_all("subdir"); - temp_dir.write("./subdir/my_file.ts", ""); - temp_dir.create_dir_all("run"); - temp_dir.write("./run/hello.ts", "console.log('Hello World');"); - temp_dir.write( - "./run/output.ts", - r#"export function output(text: string) { - console.log(text); -} -"#, - ); - context - .new_command() - .args_vec(["repl", "-A"]) - .with_pty(|mut console| { - // single quotes - console.write_line_raw("import './run/hel\t'"); - console.expect("Hello World"); - // double quotes - console.write_line_raw("import { output } from \"./run/out\t\""); - console.expect("\"./run/output.ts\""); - console.write_line_raw("output('testing output');"); - console.expect("testing output"); - }); - - // ensure when the directory changes that the suggestions come from the cwd - context - .new_command() - .args_vec(["repl", "-A"]) - .with_pty(|mut console| { - console.write_line("Deno.chdir('./subdir');"); - console.expect("undefined"); - console.write_line_raw("import '../run/he\t'"); - console.expect("Hello World"); - }); -} - -#[test] -fn pty_complete_imports_no_panic_empty_specifier() { - // does not panic when tabbing when empty - util::with_pty(&["repl", "-A"], |mut console| { - if cfg!(windows) { - console.write_line_raw("import '\t'"); - console.expect_any(&["not prefixed with", "https://deno.land"]); - } else { - console.write_raw("import '\t"); - console.expect("import 'https://deno.land"); - } - }); -} - -#[test] -fn pty_ignore_symbols() { - util::with_pty(&["repl"], |mut console| { - console.write_line_raw("Array.Symbol\t"); - console.expect("undefined"); - }); -} - -#[test] -fn pty_assign_global_this() { - util::with_pty(&["repl"], |mut console| { - console.write_line("globalThis = 40 + 2;"); - console.expect("42"); - }); -} - -#[test] -fn pty_assign_deno_keys_and_deno() { - util::with_pty(&["repl"], |mut console| { - console.write_line( - "Object.keys(Deno).forEach((key)=>{try{Deno[key] = undefined} catch {}})", - ); - console.expect("undefined"); - console.write_line("delete globalThis.Deno"); - console.expect("true"); - console.write_line("console.log('testing ' + 'this out');"); - console.expect("testing this out"); - console.expect("undefined"); - }); -} - -#[test] -fn pty_internal_repl() { - util::with_pty(&["repl"], |mut console| { - console.write_line("'Length: ' + Object.keys(globalThis).filter(k => k.startsWith('__DENO_')).length;"); - console.expect("Length: 0"); - - console.write_line_raw("__\t\t"); - console.expect("> __"); - let output = console.read_until("> __"); - assert_contains!(output, "__defineGetter__"); - // should not contain the internal repl variable - // in the `globalThis` or completions output - assert_not_contains!(output, "__DENO_"); - }); -} - -#[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.expect("🦕"); - }); -} - -#[test] -fn console_log() { - util::with_pty(&["repl"], |mut console| { - console.write_line("console.log('hello');"); - console.expect("hello"); - console.write_line("'world'"); - console.expect("\"world\""); - }); -} - -#[test] -fn object_literal() { - util::with_pty(&["repl"], |mut console| { - console.write_line("{}"); - console.expect("{}"); - console.write_line("{ foo: 'bar' }"); - console.expect("{ foo: \"bar\" }"); - }); -} - -#[test] -fn block_expression() { - util::with_pty(&["repl"], |mut console| { - console.write_line("{};"); - console.expect("undefined"); - console.write_line("{\"\"}"); - console.expect("\"\""); - }); -} - -#[test] -fn await_resolve() { - util::with_pty(&["repl"], |mut console| { - console.write_line("await Promise.resolve('done')"); - console.expect("\"done\""); - }); -} - -#[test] -fn await_timeout() { - util::with_pty(&["repl"], |mut console| { - console.write_line("await new Promise((r) => setTimeout(r, 0, 'done'))"); - console.expect("\"done\""); - }); -} - -#[test] -fn let_redeclaration() { - util::with_pty(&["repl"], |mut console| { - console.write_line("let foo = 0;"); - console.expect("undefined"); - console.write_line("foo"); - console.expect("0"); - console.write_line("let foo = 1;"); - console.expect("undefined"); - console.write_line("foo"); - console.expect("1"); - }); -} - -#[test] -fn repl_cwd() { - let context = TestContextBuilder::default().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - context - .new_command() - .args_vec(["repl", "-A"]) - .with_pty(|mut console| { - console.write_line("Deno.cwd()"); - console.expect( - temp_dir - .path() - .as_path() - .file_name() - .unwrap() - .to_str() - .unwrap(), - ); - }); -} - -#[test] -fn typescript() { - util::with_pty(&["repl"], |mut console| { - console.write_line("function add(a: number, b: number) { return a + b }"); - console.expect("undefined"); - console.write_line("const result: number = add(1, 2) as number;"); - console.expect("undefined"); - console.write_line("result"); - console.expect("3"); - }); -} - -#[test] -fn typescript_declarations() { - util::with_pty(&["repl"], |mut console| { - console.write_line("namespace Test { export enum Values { A, B, C } }"); - console.expect("undefined"); - console.write_line("Test.Values.A"); - console.expect("0"); - console.write_line("Test.Values.C"); - console.expect("2"); - console.write_line("interface MyInterface { prop: string; }"); - console.expect("undefined"); - console.write_line("type MyTypeAlias = string;"); - console.expect("undefined"); - }); -} - -#[test] -fn typescript_decorators() { - let context = TestContextBuilder::default().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "./deno.json", - r#"{ "compilerOptions": { "experimentalDecorators": true } }"#, - ); - let config_path = temp_dir.target_path().join("./deno.json"); - util::with_pty( - &["repl", "--config", config_path.to_string_lossy().as_ref()], - |mut console| { - console.write_line( - "function dec(target) { target.prototype.test = () => 2; }", - ); - console.expect("undefined"); - console.write_line("@dec class Test {}"); - console.expect("[class Test]"); - console.write_line("new Test().test()"); - console.expect("2"); - }, - ); -} - -#[test] -fn eof() { - util::with_pty(&["repl"], |mut console| { - console.write_line("1 + 2"); - console.expect("3"); - }); -} - -#[test] -fn strict() { - util::with_pty(&["repl"], |mut console| { - console.write_line("let a = {};"); - console.expect("undefined"); - console.write_line("Object.preventExtensions(a)"); - console.expect("{}"); - console.write_line("a.c = 1;"); - console.expect( - "Uncaught TypeError: Cannot add property c, object is not extensible", - ); - }); -} - -#[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() { - util::with_pty(&["repl"], |mut console| { - console.write_line("Deno.writeFileSync"); - console.expect("[Function: writeFileSync]"); - }); -} - -#[test] -fn multiline() { - util::with_pty(&["repl"], |mut console| { - console.write_line("(\n1 + 2\n)"); - console.expect("3"); - }); -} - -#[test] -fn import() { - let context = TestContextBuilder::default() - .use_copy_temp_dir("./subdir") - .build(); - context - .new_command() - .args_vec(["repl", "-A"]) - .with_pty(|mut console| { - console.write_line("import('./subdir/auto_print_hello.ts')"); - console.expect("hello!"); - }); -} - -#[test] -fn import_declarations() { - let context = TestContextBuilder::default() - .use_copy_temp_dir("./subdir") - .build(); - context - .new_command() - .args_vec(["repl", "-A"]) - .with_pty(|mut console| { - console.write_line("import './subdir/auto_print_hello.ts'"); - console.expect("hello!"); - }); -} - -#[test] -fn exports_stripped() { - util::with_pty(&["repl"], |mut console| { - console.write_line("const test = 5 + 1; export default test;"); - console.expect("6"); - console.write_line("export class Test {}"); - console.expect("undefined"); - }); -} - -#[test] -fn call_eval_unterminated() { - util::with_pty(&["repl"], |mut console| { - console.write_line("eval('{')"); - console.expect("Unexpected end of input"); - }); -} - -#[test] -fn unpaired_braces() { - util::with_pty(&["repl"], |mut console| { - for right_brace in &[")", "]", "}"] { - console.write_line(right_brace); - console.expect("Expression expected"); - } - }); -} - -#[test] -fn reference_error() { - util::with_pty(&["repl"], |mut console| { - console.write_line("not_a_variable"); - console.expect("not_a_variable is not defined"); - }); -} - -#[test] -fn syntax_error() { - util::with_pty(&["repl"], |mut console| { - console.write_line("syntax error"); - console.expect("parse error: Expected ';', '}' or <eof>"); - // ensure it keeps accepting input after - console.write_line("7 * 6"); - console.expect("42"); - }); -} - -#[test] -fn jsx_errors_without_pragma() { - util::with_pty(&["repl"], |mut console| { - console.write_line("const element = <div />;"); - console.expect("React is not defined"); - }); -} - -#[test] -fn jsx_import_source() { - let context = TestContextBuilder::default() - .use_temp_cwd() - .use_http_server() - .build(); - context - .new_command() - .args_vec(["repl", "-A"]) - .with_pty(|mut console| { - console.write_line("/** @jsxImportSource http://localhost:4545/jsx */"); - console.expect("undefined"); - console.write_line("const element = <div />;"); - console.expect("undefined"); - }); -} - -#[test] -fn type_error() { - util::with_pty(&["repl"], |mut console| { - console.write_line("console()"); - console.expect("console is not a function"); - }); -} - -#[test] -fn variable() { - util::with_pty(&["repl"], |mut console| { - console.write_line("var a = 123 + 456;"); - console.expect("undefined"); - console.write_line("a"); - console.expect("579"); - }); -} - -#[test] -fn lexical_scoped_variable() { - util::with_pty(&["repl"], |mut console| { - console.write_line("let a = 123 + 456;"); - console.expect("undefined"); - console.write_line("a"); - console.expect("579"); - }); -} - -#[test] -fn missing_deno_dir() { - use std::fs::read_dir; - let temp_dir = TempDir::new(); - let deno_dir_path = temp_dir.path().join("deno"); - let (out, err) = util::run_and_collect_output( - true, - "repl", - Some(vec!["1"]), - Some(vec![ - ("DENO_DIR".to_owned(), deno_dir_path.to_string()), - ("NO_COLOR".to_owned(), "1".to_owned()), - ]), - false, - ); - assert!(read_dir(deno_dir_path).is_ok()); - assert_ends_with!(out, "1\n"); - assert!(err.is_empty()); -} - -#[test] -fn custom_history_path() { - use std::fs::read; - let temp_dir = TempDir::new(); - let history_path = temp_dir.path().join("history.txt"); - let (out, err) = util::run_and_collect_output( - true, - "repl", - Some(vec!["1"]), - Some(vec![ - ("DENO_REPL_HISTORY".to_owned(), history_path.to_string()), - ("NO_COLOR".to_owned(), "1".to_owned()), - ]), - false, - ); - assert!(read(&history_path).is_ok()); - assert_ends_with!(out, "1\n"); - assert!(err.is_empty()); -} - -#[test] -fn disable_history_file() { - let deno_dir = util::new_deno_dir(); - let default_history_path = deno_dir.path().join("deno_history.txt"); - let (out, err) = util::run_and_collect_output( - true, - "repl", - Some(vec!["1"]), - Some(vec![ - ("DENO_DIR".to_owned(), deno_dir.path().to_string()), - ("DENO_REPL_HISTORY".to_owned(), "".to_owned()), - ("NO_COLOR".to_owned(), "1".to_owned()), - ]), - false, - ); - assert!(!default_history_path.try_exists().unwrap()); - assert_ends_with!(out, "1\n"); - assert!(err.is_empty()); -} - -#[test] -fn save_last_eval() { - util::with_pty(&["repl"], |mut console| { - console.write_line("1 + 2"); - console.expect("3"); - console.write_line("_ + 3"); - console.expect("6"); - }); -} - -#[test] -fn save_last_thrown() { - util::with_pty(&["repl"], |mut console| { - console.write_line("throw 1 + 2"); - console.expect("Uncaught 3"); - console.write_line("_error + 3"); - console.expect("6"); - }); -} - -#[test] -fn assign_underscore() { - util::with_pty(&["repl"], |mut console| { - console.write_line("_ = 1"); - console.expect("Last evaluation result is no longer saved to _."); - console.write_line("2 + 3"); - console.expect("5"); - console.write_line("_"); - console.expect("1"); - }); -} - -#[test] -fn assign_underscore_error() { - util::with_pty(&["repl"], |mut console| { - console.write_line("_error = 1"); - console.expect("Last thrown error is no longer saved to _error."); - console.write_line("throw 2"); - console.expect("Uncaught 2"); - console.write_line("_error"); - console.expect("1"); - }); -} - -#[test] -fn custom_inspect() { - util::with_pty(&["repl"], |mut console| { - console.write_line( - r#"const o = { - [Symbol.for("Deno.customInspect")]() { - throw new Error('Oops custom inspect error'); - }, - };"#, - ); - console.expect("undefined"); - console.write_line("o"); - console.expect("Oops custom inspect error"); - }); -} - -#[test] -fn eval_flag_valid_input() { - util::with_pty(&["repl", "--eval", "const t = 10;"], |mut console| { - console.write_line("t * 500"); - console.expect("5000"); - }); -} - -#[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"); -} - -#[flaky_test::flaky_test] -fn pty_clear_function() { - util::with_pty(&["repl"], |mut console| { - console.write_line("console.log('h' + 'ello');"); - console.expect_all(&["hello", "undefined"]); - console.write_line_raw("clear();"); - if cfg!(windows) { - // expect a bunch of these in the output - console.expect_raw_in_current_output( - "\r\n\u{1b}[K\r\n\u{1b}[K\r\n\u{1b}[K\r\n\u{1b}[K\r\n\u{1b}[K", - ); - } else { - console.expect_raw_in_current_output("[1;1H"); - } - console.expect("undefined"); // advance past the "clear()"'s undefined - console.expect("> "); - console.write_line("const clear = 1234 + 2000;"); - console.expect("undefined"); - console.write_line("clear;"); - console.expect("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_raw("a\t\t"); - console.expect_all(&["addEventListener", "alert", "atob"]); - }); - // If the last character is whitespace, we just insert a tab - util::with_pty(&["repl"], |mut console| { - console.write_line("const a = 5;"); - console.expect("undefined"); - console.write_raw("a; \t\ta + 2;\n"); // last character is whitespace - console.expect_any(&[ - // windows - "a; a + 2;", - // unix - "a; \t\ta + 2;", - ]); - }); -} - -#[test] -fn repl_error() { - util::with_pty(&["repl"], |mut console| { - console.write_line("console.log(1);"); - console.expect_all(&["1", "undefined"]); - console.write_line(r#"throw new Error("foo");"#); - console.expect("Uncaught Error: foo"); - console.expect(" at <anonymous>"); - console.write_line("console.log(2);"); - console.expect("2"); - }); -} - -#[flaky_test::flaky_test] -fn repl_reject() { - util::with_pty(&["repl"], |mut console| { - console.write_line("console.log(1);"); - console.expect_all(&["1", "undefined"]); - console.write_line(r#"Promise.reject(new Error("foo"));"#); - console.expect("Promise {"); - console.expect(" <rejected> Error: foo"); - console.expect("Uncaught Error: foo"); - console.expect(" at <anonymous>"); - console.write_line("console.log(2);"); - console.expect("2"); - console.write_line(r#"throw "hello";"#); - console.expect(r#"Uncaught "hello""#); - console.write_line(r#"throw `hello ${"world"}`;"#); - console.expect(r#"Uncaught "hello world""#); - }); -} - -#[flaky_test::flaky_test] -fn repl_report_error() { - util::with_pty(&["repl"], |mut console| { - console.write_line("console.log(1);"); - console.expect_all(&["1", "undefined"]); - console.write_line(r#"reportError(new Error("foo"));"#); - console.expect("undefined"); - console.expect("Uncaught Error: foo"); - console.expect(" at <anonymous>"); - console.write_line("console.log(2);"); - console.expect("2"); - }); -} - -#[flaky_test::flaky_test] -fn repl_error_undefined() { - util::with_pty(&["repl"], |mut console| { - console.write_line(r#"throw undefined;"#); - console.expect("Uncaught undefined"); - console.write_line(r#"Promise.reject();"#); - console.expect("Promise { <rejected> undefined }"); - console.expect("Uncaught undefined"); - console.write_line(r#"reportError(undefined);"#); - console.expect("undefined"); - console.expect("Uncaught undefined"); - }); -} - -#[test] -fn pty_aggregate_error() { - util::with_pty(&["repl"], |mut console| { - console.write_line("await Promise.any([])"); - console.expect("AggregateError"); - }); -} - -#[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 repl_unit_tests() { - util::with_pty(&["repl"], |mut console| { - console.write_line_raw( - "\ - console.log('Hello from outside of test!'); \ - Deno.test('test1', async (t) => { \ - console.log('Hello from inside of test!'); \ - await t.step('step1', () => {}); \ - }); \ - Deno.test('test2', () => { \ - throw new Error('some message'); \ - }); \ - console.log('Hello again from outside of test!'); \ - ", - ); - - console.expect("Hello from outside of test!"); - console.expect("Hello again from outside of test!"); - // FIXME(nayeemrmn): REPL unit tests don't support output capturing. - console.expect("Hello from inside of test!"); - console.expect("test1 ..."); - console.expect(" step1 ... ok ("); - console.expect("test1 ... ok ("); - console.expect("test2 ... FAILED ("); - console.expect("ERRORS"); - console.expect("test2 => <anonymous>:6:6"); - console.expect("error: Error: some message"); - console.expect(" at <anonymous>:7:9"); - console.expect("FAILURES"); - console.expect("test2 => <anonymous>:6:6"); - console.expect("FAILED | 1 passed (1 step) | 1 failed ("); - console.expect("undefined"); - - console.write_line("Deno.test('test2', () => {});"); - - console.expect("test2 ... ok ("); - console.expect("ok | 1 passed | 0 failed ("); - console.expect("undefined"); - }); -} - -#[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 temp_dir = TempDir::new(); - env_vars.push(("DENO_DIR".to_string(), temp_dir.path().to_string())); - - { - 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: null prototype] {"); - assert_contains!(out, "Chalk: [class 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.clone()), - true, - ); - - assert_contains!( - out, - "error: npm package 'asdfawe52345asdf' does not exist" - ); - assert!(err.is_empty()); - } - - { - let (out, err) = util::run_and_collect_output_with_args( - true, - vec!["repl", "--quiet", "--allow-read", "--allow-env"], - Some(vec![ - "import path from 'node:path';", - "path.isGlob('asdf') ? 'yes' : 'no'", - ]), - Some(env_vars.clone()), - true, - ); - - assert_contains!(out, "no"); - assert!(err.is_empty()); - } -} - -#[test] -fn pty_tab_indexable_props() { - util::with_pty(&["repl"], |mut console| { - console.write_line("const arr = [1, 2, 3]"); - console.expect("undefined"); - console.write_raw("arr.\t\t"); - console.expect("> arr."); - let output = console.read_until("> arr."); - assert_contains!(output, "constructor"); - assert_contains!(output, "sort"); - assert_contains!(output, "at"); - assert_not_contains!(output, "0", "1", "2"); - }); -} - -#[flaky_test::flaky_test] -fn package_json_uncached_no_error() { - let test_context = TestContextBuilder::for_npm() - .use_temp_cwd() - .use_http_server() - .env("RUST_BACKTRACE", "1") - .build(); - let temp_dir = test_context.temp_dir(); - temp_dir.write( - "package.json", - r#"{ - "dependencies": { - "@denotest/esm-basic": "1.0.0" - } -} -"#, - ); - test_context.new_command().with_pty(|mut console| { - console.write_line("console.log(123 + 456);"); - console.expect_all(&["579", "undefined"]); - assert_not_contains!( - console.all_output(), - "Could not set npm package requirements", - ); - - // should support getting the package now though - console - .write_line("import { getValue, setValue } from '@denotest/esm-basic';"); - console.expect_all(&["undefined", "Download"]); - console.write_line("setValue(12 + 30);"); - console.expect("undefined"); - console.write_line("getValue()"); - console.expect("42"); - - assert!(temp_dir.path().join("node_modules").exists()); - }); -} - -#[test] -fn closed_file_pre_load_does_not_occur() { - TestContext::default() - .new_command() - .args_vec(["repl", "-A", "--log-level=debug"]) - .with_pty(|console| { - assert_contains!(console.all_output(), "Skipping document preload.",); - }); -} - -#[test] -fn env_file() { - TestContext::default() - .new_command() - .args_vec([ - "repl", - "--env=env", - "--allow-env", - "--eval", - "console.log(Deno.env.get('FOO'))", - ]) - .with_pty(|console| { - assert_contains!(console.all_output(), "BAR",); - }); -} - -// Regression test for https://github.com/denoland/deno/issues/20528 -#[test] -fn pty_promise_was_collected_regression_test() { - let (out, err) = util::run_and_collect_output_with_args( - true, - vec!["repl"], - Some(vec!["new Uint8Array(64 * 1024 * 1024)"]), - None, - false, - ); - - assert_contains!(out, "Uint8Array(67108864)"); - assert!(err.is_empty()); -} diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs deleted file mode 100644 index 298ce1715..000000000 --- a/cli/tests/integration/run_tests.rs +++ /dev/null @@ -1,5142 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use bytes::Bytes; -use deno_core::serde_json::json; -use deno_core::url; -use deno_fetch::reqwest; -use pretty_assertions::assert_eq; -use std::io::Read; -use std::io::Write; -use std::process::Command; -use std::process::Stdio; -use std::time::Duration; -use test_util as util; -use test_util::TempDir; -use trust_dns_client::serialize::txt::Lexer; -use trust_dns_client::serialize::txt::Parser; -use util::assert_contains; -use util::assert_not_contains; -use util::env_vars_for_npm_tests; -use util::PathRef; -use util::TestContext; -use util::TestContextBuilder; - -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_check { - args: "run --quiet --reload --check run/017_import_redirect.ts", - output: "run/017_import_redirect.ts.out", -}); - -itest!(_017_import_redirect_vendor_dir { - args: - "run --quiet --reload --vendor --check $TESTDATA/run/017_import_redirect.ts", - output: "run/017_import_redirect.ts.out", - temp_cwd: true, -}); - -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", -}); - -itest!(_025_hrtime { - args: "run --quiet --allow-hrtime --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!(_027_redirect_typescript_vendor_dir { - args: - "run --quiet --reload --vendor $TESTDATA/run/027_redirect_typescript.ts", - output: "run/027_redirect_typescript.ts.out", - http_server: true, - temp_cwd: 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_in_config_file { - args: "run --reload --config=import_maps/config.json import_maps/test.ts", - output: "run/033_import_map_in_config_file.out", -}); - -itest!(_033_import_map_in_flag_has_precedence { - args: "run --quiet --reload --import-map=import_maps/import_map_invalid.json --config=import_maps/config.json import_maps/test.ts", - output: "run/033_import_map_in_flag_has_precedence.out", - exit_code: 1, -}); - -itest!(_033_import_map_remote { - args: - "run --quiet --reload --import-map=http://127.0.0.1:4545/import_maps/import_map_remote.json import_maps/test_remote.ts", - output: "run/033_import_map_remote.out", - http_server: true, -}); - -itest!(_033_import_map_vendor_dir_remote { - args: - "run --quiet --reload --import-map=http://127.0.0.1:4545/import_maps/import_map_remote.json --vendor $TESTDATA/import_maps/test_remote.ts", - output: "run/033_import_map_remote.out", - http_server: true, - temp_cwd: true, -}); - -itest!(_033_import_map_data_uri { - args: - "run --quiet --reload --import-map=data:application/json;charset=utf-8;base64,ewogICJpbXBvcnRzIjogewogICAgInRlc3Rfc2VydmVyLyI6ICJodHRwOi8vbG9jYWxob3N0OjQ1NDUvIgogIH0KfQ== run/import_maps/test_data.ts", - output: "run/import_maps/test_data.ts.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, -}); - -itest!(_045_proxy { - args: "run -L debug --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 output = util::deno_cmd_with_deno_dir(&deno_dir) - .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 output = util::deno_cmd_with_deno_dir(&deno_dir) - .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 { hello: \"deno\", length: 1 }\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 context = TestContext::default(); - - context - .new_command() - .args( - "run --config run/webstorage/config_a.jsonc run/webstorage/fixture.ts", - ) - .run() - .assert_matches_text("Storage { length: 0 }\n"); - - context - .new_command() - .args("run --config run/webstorage/config_b.jsonc run/webstorage/logger.ts") - .run() - .assert_matches_text("Storage { length: 0 }\n"); - - context - .new_command() - .args("run --config run/webstorage/config_a.jsonc run/webstorage/logger.ts") - .run() - .assert_matches_text("Storage { hello: \"deno\", length: 1 }\n"); -} - -// tests to ensure `--config` does not effect persisted storage when a -// `--location` is provided. -#[test] -fn webstorage_location_precedes_config() { - let context = TestContext::default(); - - context.new_command() - .args("run --location https://example.com/a.ts --config run/webstorage/config_a.jsonc run/webstorage/fixture.ts") - .run() - .assert_matches_text("Storage { length: 0 }\n"); - - context.new_command() - .args("run --location https://example.com/b.ts --config run/webstorage/config_b.jsonc run/webstorage/logger.ts") - .run() - .assert_matches_text("Storage { hello: \"deno\", length: 1 }\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 context = TestContext::default(); - - context - .new_command() - .args("run run/webstorage/fixture.ts") - .run() - .assert_matches_text("Storage { length: 0 }\n"); - - context - .new_command() - .args("run run/webstorage/logger.ts") - .run() - .assert_matches_text("Storage { length: 0 }\n"); - - context - .new_command() - .args("run run/webstorage/fixture.ts") - .run() - .assert_matches_text("Storage { hello: \"deno\", length: 1 }\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"); - faulty_map_path.parent().create_dir_all(); - faulty_map_path.write(r#"{\"version\":3,\"file\":\"\",\"sourceRoot\":\"\",\"sources\":[\"http://localhost:4545/083_legacy_external_source_map.ts\"],\"names\":[],\"mappings\":\";AAAA,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC\"}"#); - 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!(dynamic_import_concurrent_non_statically_analyzable { - args: "run --allow-read --allow-net --quiet run/dynamic_import_concurrent_non_statically_analyzable/main.ts", - output: "run/dynamic_import_concurrent_non_statically_analyzable/main.out", - http_server: true, -}); - -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.Command 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() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/090_run_permissions_request.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "┌ ⚠️ Deno requests run access to \"ls\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-run to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)", - )); - console.write_line_raw("y"); - console.expect("Granted run access to \"ls\"."); - console.expect(concat!( - "┌ ⚠️ Deno requests run access to \"cat\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-run to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)", - )); - console.write_line_raw("n"); - console.expect("Denied run access to \"cat\"."); - console.expect("granted"); - console.expect("denied"); - }); -} - -#[test] -fn _090_run_permissions_request_sync() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/090_run_permissions_request_sync.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "┌ ⚠️ Deno requests run access to \"ls\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-run to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)", - )); - console.write_line_raw("y"); - console.expect("Granted run access to \"ls\"."); - console.expect(concat!( - "┌ ⚠️ Deno requests run access to \"cat\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-run to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)", - )); - console.write_line_raw("n"); - console.expect("Denied run access to \"cat\"."); - console.expect("granted"); - console.expect("denied"); - }); -} - -#[test] -fn permissions_prompt_allow_all() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/permissions_prompt_allow_all.ts"]) - .with_pty(|mut console| { - // "run" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests run access to \"FOO\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-run to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)", - )); - console.write_line_raw("A"); - console.expect("✅ Granted all run access."); - // "read" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests read access to \"FOO\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("A"); - console.expect("✅ Granted all read access."); - // "write" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests write access to \"FOO\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-write to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all write permissions)", - )); - console.write_line_raw("A"); - console.expect("✅ Granted all write access."); - // "net" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests net access to \"foo\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-net to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions)", - )); - console.write_line_raw("A\n"); - console.expect("✅ Granted all net access."); - // "env" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests env access to \"FOO\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-env to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", - )); - console.write_line_raw("A\n"); - console.expect("✅ Granted all env access."); - // "sys" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests sys access to \"loadavg\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-sys to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all sys permissions)", - )); - console.write_line_raw("A\n"); - console.expect("✅ Granted all sys access."); - // "ffi" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests ffi access to \"FOO\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-ffi to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all ffi permissions)", - )); - console.write_line_raw("A\n"); - console.expect("✅ Granted all ffi access.") - }, - ); -} - -#[test] -fn permissions_prompt_allow_all_2() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/permissions_prompt_allow_all_2.ts"]) - .with_pty(|mut console| { - // "env" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests env access to \"FOO\".\r\n", - "├ Run again with --allow-env to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", - )); - console.write_line_raw("A"); - console.expect("✅ Granted all env access."); - - // "sys" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests sys access to \"loadavg\".\r\n", - "├ Requested by `Deno.loadavg()` API.\r\n", - "├ Run again with --allow-sys to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all sys permissions)", - )); - console.write_line_raw("A"); - console.expect("✅ Granted all sys access."); - - // "read" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests read access to <CWD>.\r\n", - "├ Requested by `Deno.cwd()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("A"); - console.expect("✅ Granted all read access."); - }); -} - -#[test] -fn permissions_prompt_allow_all_lowercase_a() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/permissions_prompt_allow_all.ts"]) - .with_pty(|mut console| { - // "run" permissions - console.expect(concat!( - "┌ ⚠️ Deno requests run access to \"FOO\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-run to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)", - )); - console.write_line_raw("a"); - console.expect("Unrecognized option."); - }); -} - -#[test] -fn permission_request_long() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/permission_request_long.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "❌ Permission prompt length (100017 bytes) was larger than the configured maximum length (10240 bytes): denying request.\r\n", - "❌ WARNING: This may indicate that code is trying to bypass or hide permission check requests.\r\n", - "❌ Run again with --allow-read to bypass this check if this is really what you want to do.\r\n", - )); - }); -} - -itest!(deny_all_permission_args { - args: "run --deny-env --deny-read --deny-write --deny-ffi --deny-run --deny-sys --deny-net --deny-hrtime run/deny_all_permission_args.js", - output: "run/deny_all_permission_args.out", -}); - -itest!(deny_some_permission_args { - args: "run --allow-env --deny-env=FOO --allow-read --deny-read=/foo --allow-write --deny-write=/foo --allow-ffi --deny-ffi=/foo --allow-run --deny-run=foo --allow-sys --deny-sys=hostname --allow-net --deny-net=127.0.0.1 --allow-hrtime --deny-hrtime run/deny_some_permission_args.js", - output: "run/deny_some_permission_args.out", -}); - -#[test] -fn permissions_cache() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/permissions_cache.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "prompt\r\n", - "┌ ⚠️ Deno requests read access to \"foo\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("y"); - console.expect("✅ Granted read access to \"foo\"."); - console.expect("granted"); - console.expect("prompt"); - }); -} - -itest!(env_file { - args: "run --env=env --allow-env run/env_file.ts", - output: "run/env_file.out", -}); - -itest!(env_file_missing { - args: "run --env=missing --allow-env run/env_file.ts", - output: "run/env_file_missing.out", -}); - -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", -}); - -itest!(lock_write_fetch { - args: - "run --quiet --allow-read --allow-write --allow-env --allow-run 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 --quiet --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 --quiet --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, -}); - -#[test] -fn lock_no_declaration_files() { - let context = TestContextBuilder::new() - .use_temp_cwd() - .use_http_server() - .build(); - let output = context - .new_command() - .args("cache --lock --lock-write $TESTDATA/lockfile/no_dts/main.ts") - .run(); - output.assert_matches_file("lockfile/no_dts/main.cache.out"); - let lockfile = context.temp_dir().path().join("deno.lock"); - lockfile.assert_matches_file("lockfile/no_dts/deno.lock.out"); -} - -#[test] -fn lock_redirects() { - let context = TestContextBuilder::new() - .use_temp_cwd() - .use_http_server() - .add_npm_env_vars() - .build(); - let temp_dir = context.temp_dir(); - temp_dir.write("deno.json", "{}"); // cause a lockfile to be created - temp_dir.write( - "main.ts", - "import 'http://localhost:4546/run/001_hello.js';", - ); - context - .new_command() - .args("run main.ts") - .run() - .skip_output_check(); - let initial_lockfile_text = r#"{ - "version": "3", - "redirects": { - "http://localhost:4546/run/001_hello.js": "http://localhost:4545/run/001_hello.js" - }, - "remote": { - "http://localhost:4545/run/001_hello.js": "c479db5ea26965387423ca438bb977d0b4788d5901efcef52f69871e4c1048c5" - } -} -"#; - assert_eq!(temp_dir.read_to_string("deno.lock"), initial_lockfile_text); - context - .new_command() - .args("run main.ts") - .run() - .assert_matches_text("Hello World\n"); - assert_eq!(temp_dir.read_to_string("deno.lock"), initial_lockfile_text); - - // now try changing where the redirect occurs in the lockfile - temp_dir.write("deno.lock", r#"{ - "version": "3", - "redirects": { - "http://localhost:4546/run/001_hello.js": "http://localhost:4545/echo.ts" - }, - "remote": { - "http://localhost:4545/run/001_hello.js": "c479db5ea26965387423ca438bb977d0b4788d5901efcef52f69871e4c1048c5" - } -} -"#); - - // also, add some npm dependency to ensure it doesn't end up in - // the redirects as they're currently stored separately - temp_dir.write( - "main.ts", - "import 'http://localhost:4546/run/001_hello.js';\n import 'npm:@denotest/esm-basic';\n", - ); - - // it should use the echo script instead - context - .new_command() - .args("run main.ts Hi there") - .run() - .assert_matches_text( - concat!( - "Download http://localhost:4545/echo.ts\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz\n", - "Hi, there", - )); - util::assertions::assert_wildcard_match( - &temp_dir.read_to_string("deno.lock"), - r#"{ - "version": "3", - "packages": { - "specifiers": { - "npm:@denotest/esm-basic": "npm:@denotest/esm-basic@1.0.0" - }, - "npm": { - "@denotest/esm-basic@1.0.0": { - "integrity": "sha512-[WILDCARD]", - "dependencies": {} - } - } - }, - "redirects": { - "http://localhost:4546/run/001_hello.js": "http://localhost:4545/echo.ts" - }, - "remote": { - "http://localhost:4545/echo.ts": "829eb4d67015a695d70b2a33c78b631b29eea1dbac491a6bfcf394af2a2671c2", - "http://localhost:4545/run/001_hello.js": "c479db5ea26965387423ca438bb977d0b4788d5901efcef52f69871e4c1048c5" - } -} -"#, - ); -} - -#[test] -fn lock_deno_json_package_json_deps() { - let context = TestContextBuilder::new() - .use_temp_cwd() - .use_http_server() - .add_npm_env_vars() - .add_jsr_env_vars() - .build(); - let temp_dir = context.temp_dir().path(); - let deno_json = temp_dir.join("deno.json"); - let package_json = temp_dir.join("package.json"); - - // add a jsr and npm dependency - deno_json.write_json(&json!({ - "imports": { - "esm-basic": "npm:@denotest/esm-basic", - "module_graph": "jsr:@denotest/module_graph@1.4", - } - })); - let main_ts = temp_dir.join("main.ts"); - main_ts.write("import 'esm-basic'; import 'module_graph';"); - context - .new_command() - .args("cache main.ts") - .run() - .skip_output_check(); - let lockfile = temp_dir.join("deno.lock"); - let esm_basic_integrity = - get_lockfile_npm_package_integrity(&lockfile, "@denotest/esm-basic@1.0.0"); - lockfile.assert_matches_json(json!({ - "version": "3", - "packages": { - "specifiers": { - "jsr:@denotest/module_graph@1.4": "jsr:@denotest/module_graph@1.4.0", - "npm:@denotest/esm-basic": "npm:@denotest/esm-basic@1.0.0" - }, - "jsr": { - "@denotest/module_graph@1.4.0": {} - }, - "npm": { - "@denotest/esm-basic@1.0.0": { - "integrity": esm_basic_integrity, - "dependencies": {} - } - } - }, - "remote": { - "http://127.0.0.1:4250/@denotest/module_graph/1.4.0/mod.ts": "5b0ce36e08d759118200d8b4627627b5a89b6261fbb0598e6961a6b287abb699", - "http://127.0.0.1:4250/@denotest/module_graph/1.4.0/other.ts": "9ce27ca439cb0e218b6e1ec26c043dbc0b54c9babc4cb432df478dd1721faade" - }, - "workspace": { - "dependencies": [ - "jsr:@denotest/module_graph@1.4", - "npm:@denotest/esm-basic" - ] - } - })); - - // now remove the npm dependency from the deno.json and move - // it to a package.json that uses an alias - deno_json.write_json(&json!({ - "imports": { - "module_graph": "jsr:@denotest/module_graph@1.4", - } - })); - package_json.write_json(&json!({ - "dependencies": { - "esm-basic": "npm:@denotest/esm-basic" - } - })); - context - .new_command() - .args("cache main.ts") - .run() - .skip_output_check(); - main_ts.write("import 'module_graph';"); - context - .new_command() - // ensure this doesn't clear out packageJson below - .args("cache --no-npm main.ts") - .run() - .skip_output_check(); - lockfile.assert_matches_json(json!({ - "version": "3", - "packages": { - "specifiers": { - "jsr:@denotest/module_graph@1.4": "jsr:@denotest/module_graph@1.4.0", - "npm:@denotest/esm-basic": "npm:@denotest/esm-basic@1.0.0" - }, - "jsr": { - "@denotest/module_graph@1.4.0": {} - }, - "npm": { - "@denotest/esm-basic@1.0.0": { - "integrity": esm_basic_integrity, - "dependencies": {} - } - } - }, - "remote": { - "http://127.0.0.1:4250/@denotest/module_graph/1.4.0/mod.ts": "5b0ce36e08d759118200d8b4627627b5a89b6261fbb0598e6961a6b287abb699", - "http://127.0.0.1:4250/@denotest/module_graph/1.4.0/other.ts": "9ce27ca439cb0e218b6e1ec26c043dbc0b54c9babc4cb432df478dd1721faade" - }, - "workspace": { - "dependencies": [ - "jsr:@denotest/module_graph@1.4" - ], - "packageJson": { - "dependencies": [ - "npm:@denotest/esm-basic" - ] - } - } - })); - - // now remove the package.json - package_json.remove_file(); - - // cache and it will remove the package.json - context - .new_command() - .args("cache main.ts") - .run() - .skip_output_check(); - lockfile.assert_matches_json(json!({ - "version": "3", - "packages": { - "specifiers": { - "jsr:@denotest/module_graph@1.4": "jsr:@denotest/module_graph@1.4.0", - }, - "jsr": { - "@denotest/module_graph@1.4.0": {} - } - }, - "remote": { - "http://127.0.0.1:4250/@denotest/module_graph/1.4.0/mod.ts": "5b0ce36e08d759118200d8b4627627b5a89b6261fbb0598e6961a6b287abb699", - "http://127.0.0.1:4250/@denotest/module_graph/1.4.0/other.ts": "9ce27ca439cb0e218b6e1ec26c043dbc0b54c9babc4cb432df478dd1721faade" - }, - "workspace": { - "dependencies": [ - "jsr:@denotest/module_graph@1.4" - ] - } - })); - - // now remove the deps from the deno.json - deno_json.write("{}"); - main_ts.write(""); - context - .new_command() - .args("cache main.ts") - .run() - .skip_output_check(); - - lockfile.assert_matches_json(json!({ - "version": "3", - "remote": {} - })); -} - -#[test] -fn lock_deno_json_package_json_deps_workspace() { - let context = TestContextBuilder::new() - .use_temp_cwd() - .use_http_server() - .add_npm_env_vars() - .add_jsr_env_vars() - .build(); - let temp_dir = context.temp_dir().path(); - - // deno.json - let deno_json = temp_dir.join("deno.json"); - deno_json.write_json(&json!({})); - - // package.json - let package_json = temp_dir.join("package.json"); - package_json.write_json(&json!({ - "workspaces": ["package-a"], - "dependencies": { - "@denotest/cjs-default-export": "1" - } - })); - // main.ts - let main_ts = temp_dir.join("main.ts"); - main_ts.write("import '@denotest/cjs-default-export';"); - - // package-a/package.json - let a_package = temp_dir.join("package-a"); - a_package.create_dir_all(); - let a_package_json = a_package.join("package.json"); - a_package_json.write_json(&json!({ - "dependencies": { - "@denotest/esm-basic": "1" - } - })); - // package-a/main.ts - let main_ts = a_package.join("main.ts"); - main_ts.write("import '@denotest/esm-basic';"); - context - .new_command() - .args("run package-a/main.ts") - .run() - .skip_output_check(); - let lockfile = temp_dir.join("deno.lock"); - let esm_basic_integrity = - get_lockfile_npm_package_integrity(&lockfile, "@denotest/esm-basic@1.0.0"); - - // no "workspace" because deno isn't smart enough to figure this out yet - // since it discovered the package.json in a folder different from the lockfile - lockfile.assert_matches_json(json!({ - "version": "3", - "packages": { - "specifiers": { - "npm:@denotest/esm-basic@1": "npm:@denotest/esm-basic@1.0.0" - }, - "npm": { - "@denotest/esm-basic@1.0.0": { - "integrity": esm_basic_integrity, - "dependencies": {} - } - } - }, - "remote": {}, - })); - - // run a command that causes discovery of the root package.json beside the lockfile - context - .new_command() - .args("run main.ts") - .run() - .skip_output_check(); - // now we should see the dependencies - let cjs_default_export_integrity = get_lockfile_npm_package_integrity( - &lockfile, - "@denotest/cjs-default-export@1.0.0", - ); - let expected_lockfile = json!({ - "version": "3", - "packages": { - "specifiers": { - "npm:@denotest/cjs-default-export@1": "npm:@denotest/cjs-default-export@1.0.0", - "npm:@denotest/esm-basic@1": "npm:@denotest/esm-basic@1.0.0" - }, - "npm": { - "@denotest/cjs-default-export@1.0.0": { - "integrity": cjs_default_export_integrity, - "dependencies": {} - }, - "@denotest/esm-basic@1.0.0": { - "integrity": esm_basic_integrity, - "dependencies": {} - } - } - }, - "remote": {}, - "workspace": { - "packageJson": { - "dependencies": [ - "npm:@denotest/cjs-default-export@1" - ] - } - } - }); - lockfile.assert_matches_json(expected_lockfile.clone()); - - // now run the command again in the package with the nested package.json - context - .new_command() - .args("run package-a/main.ts") - .run() - .skip_output_check(); - // the lockfile should stay the same as the above because the package.json - // was found in a different directory - lockfile.assert_matches_json(expected_lockfile.clone()); -} - -fn get_lockfile_npm_package_integrity( - lockfile: &PathRef, - package_name: &str, -) -> String { - // todo(dsherret): it would be nice if the test server didn't produce - // different hashes depending on what operating system it's running on - lockfile - .read_json_value() - .get("packages") - .unwrap() - .get("npm") - .unwrap() - .get(package_name) - .unwrap() - .get("integrity") - .unwrap() - .as_str() - .unwrap() - .to_string() -} - -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 --check=all --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 --check=all --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 run/set_exit_code_0.ts", - output_str: Some(""), - exit_code: 0, -}); - -itest!(set_exit_code_1 { - args: "run --no-check run/set_exit_code_1.ts", - output_str: Some(""), - exit_code: 42, -}); - -itest!(set_exit_code_2 { - args: "run --no-check 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 --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 run/deno_exit_tampering.ts", - output_str: Some(""), - exit_code: 42, -}); - -itest!(heapstats { - args: "run --quiet --v8-flags=--expose-gc run/heapstats.js", - output: "run/heapstats.js.out", -}); - -itest!(finalization_registry { - args: "run --quiet --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", - http_server: true, -}); - -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/decorators/experimental/no_check/main.ts", - output: "run/decorators/experimental/no_check/main.out", -}); - -itest!(decorators_tc39_proposal { - args: "run --quiet --reload --check run/decorators/tc39_proposal/main.ts", - output: "run/decorators/tc39_proposal/main.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/decorators/experimental/runtime/main.ts", - output: "run/decorators/experimental/runtime/main.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", -}); - -#[test] -fn type_directives_js_main() { - let context = TestContext::default(); - let output = context - .new_command() - .args("run --reload -L debug --check run/type_directives_js_main.js") - .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch() - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); - let output = context - .new_command() - .args("run --reload -L debug run/type_directives_js_main.js") - .run(); - assert_not_contains!(output.combined_output(), "type_reference.d.ts"); -} - -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/decorators/experimental/ts/main.ts", - output: "run/decorators/experimental/ts/main.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_env_run { - envs: vec![("DENO_V8_FLAGS".to_string(), "--expose-gc".to_string())], - args: "run 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_js { - args: "run --reload run/unstable.js", - output: "run/unstable_disabled_js.out", -}); - -itest!(unstable_enabled_js { - args: "run --quiet --reload --unstable-fs run/unstable.ts", - output: "run/unstable_enabled_js.out", -}); - -itest!(unstable_worker { - args: "run --reload --quiet --allow-read run/unstable_worker.ts", - output: "run/unstable_worker.ts.out", -}); - -itest!(unstable_worker_options_disabled { - args: "run --quiet --reload --allow-read run/unstable_worker_options.js", - output: "run/unstable_worker_options.disabled.out", - exit_code: 70, -}); - -itest!(unstable_worker_options_enabled { - args: "run --quiet --reload --allow-read --unstable-worker-options run/unstable_worker_options.js", - output: "run/unstable_worker_options.enabled.out", -}); - -itest!(unstable_broadcast_channel_disabled { - args: "run --quiet --reload --allow-read run/unstable_broadcast_channel.js", - output: "run/unstable_broadcast_channel.disabled.out", -}); - -itest!(unstable_broadcast_channel_enabled { - args: "run --quiet --reload --allow-read --unstable-broadcast-channel run/unstable_broadcast_channel.js", - output: "run/unstable_broadcast_channel.enabled.out", -}); - -itest!(unstable_cron_disabled { - args: "run --quiet --reload --allow-read run/unstable_cron.js", - output: "run/unstable_cron.disabled.out", -}); - -itest!(unstable_cron_enabled { - args: - "run --quiet --reload --allow-read --unstable-cron run/unstable_cron.js", - output: "run/unstable_cron.enabled.out", -}); - -itest!(unstable_ffi_disabled { - args: "run --quiet --reload --allow-read run/unstable_ffi.js", - output: "run/unstable_ffi.disabled.out", -}); - -itest!(unstable_ffi_enabled { - args: "run --quiet --reload --allow-read --unstable-ffi run/unstable_ffi.js", - output: "run/unstable_ffi.enabled.out", -}); - -itest!(unstable_fs_disabled { - args: "run --quiet --reload --allow-read run/unstable_fs.js", - output: "run/unstable_fs.disabled.out", -}); - -itest!(unstable_fs_enabled { - args: "run --quiet --reload --allow-read --unstable-fs run/unstable_fs.js", - output: "run/unstable_fs.enabled.out", -}); - -itest!(unstable_http_disabled { - args: "run --quiet --reload --allow-read run/unstable_http.js", - output: "run/unstable_http.disabled.out", -}); - -itest!(unstable_http_enabled { - args: - "run --quiet --reload --allow-read --unstable-http run/unstable_http.js", - output: "run/unstable_http.enabled.out", -}); - -itest!(unstable_net_disabled { - args: "run --quiet --reload --allow-read run/unstable_net.js", - output: "run/unstable_net.disabled.out", -}); - -itest!(unstable_net_enabled { - args: "run --quiet --reload --allow-read --unstable-net run/unstable_net.js", - output: "run/unstable_net.enabled.out", -}); - -itest!(unstable_kv_disabled { - args: "run --quiet --reload --allow-read run/unstable_kv.js", - output: "run/unstable_kv.disabled.out", -}); - -itest!(unstable_kv_enabled { - args: "run --quiet --reload --allow-read --unstable-kv run/unstable_kv.js", - output: "run/unstable_kv.enabled.out", -}); - -itest!(unstable_webgpu_disabled { - args: "run --quiet --reload --allow-read run/unstable_webgpu.js", - output: "run/unstable_webgpu.disabled.out", -}); - -itest!(unstable_webgpu_enabled { - args: - "run --quiet --reload --allow-read --unstable-webgpu run/unstable_webgpu.js", - output: "run/unstable_webgpu.enabled.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_precompile_import_map { - args: "run --reload --check --import-map jsx/import-map.json --no-lock --config jsx/deno-jsx-precompile.jsonc run/jsx_precompile/no_pragma.tsx", - output: "run/jsx_precompile/no_pragma.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_pragma_with_config_vendor_dir { - args: "run --reload --config jsx/deno-jsx.jsonc --no-lock --vendor $TESTDATA/run/jsx_import_source_pragma.tsx", - output: "run/jsx_import_source.out", - http_server: true, - temp_cwd: true, - copy_temp_dir: Some("jsx/"), -}); - -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, -}); - -itest!(single_compile_with_reload { - args: "run --reload --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_vendor_dir { - args: - "run --config run/checkjs.tsconfig.json --check --vendor $TESTDATA/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 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_attributes/json_with_shebang.ts", - output: "import_attributes/json_with_shebang.ts.out", - exit_code: 1, -}); - -itest!(shebang_with_json_imports_swc { - args: "run --quiet --no-check import_attributes/json_with_shebang.ts", - output: "import_attributes/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") - .piped_output() - .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 = PathRef::new(std::path::Path::new(stdout_str)).canonicalize(); - let expected = util::deno_exe_path().canonicalize(); - assert_eq!(expected, actual); -} - -#[test] -fn run_from_stdin_defaults_to_ts() { - let source_code = r#" -interface Lollipop { - _: number; -} -console.log("executing typescript"); -"#; - - let mut p = util::deno_cmd() - .arg("run") - .arg("--check") - .arg("-") - .stdin(std::process::Stdio::piped()) - .stdout_piped() - .spawn() - .unwrap(); - let stdin = p.stdin.as_mut().unwrap(); - stdin.write_all(source_code.as_bytes()).unwrap(); - let result = p.wait_with_output().unwrap(); - assert!(result.status.success()); - let stdout_str = std::str::from_utf8(&result.stdout).unwrap().trim(); - assert_eq!(stdout_str, "executing typescript"); -} - -#[test] -fn run_from_stdin_ext() { - let source_code = r#" -let i = 123; -i = "hello" -console.log("executing javascript"); -"#; - - let mut p = util::deno_cmd() - .args("run --ext js --check -") - .stdin(std::process::Stdio::piped()) - .stdout_piped() - .spawn() - .unwrap(); - let stdin = p.stdin.as_mut().unwrap(); - stdin.write_all(source_code.as_bytes()).unwrap(); - let result = p.wait_with_output().unwrap(); - assert!(result.status.success()); - let stdout_str = std::str::from_utf8(&result.stdout).unwrap().trim(); - assert_eq!(stdout_str, "executing javascript"); -} - -#[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: test_util::PathRef, - 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().to_string(); - let deno_script_path = script_path.to_string(); - 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_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_piped() - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - assert!(output.status.success()); - assert!(!output.stderr.is_empty()); -} - -#[test] -fn dont_cache_on_check_fail() { - let context = TestContext::default(); - let output = context - .new_command() - .args("run --check=all --reload run/error_003_typescript.ts") - .split_output() - .run(); - assert!(!output.stderr().is_empty()); - output.skip_stdout_check(); - output.assert_exit_code(1); - - let output = context - .new_command() - .args("run --check=all run/error_003_typescript.ts") - .split_output() - .run(); - assert!(!output.stderr().is_empty()); - output.skip_stdout_check(); - output.assert_exit_code(1); -} - -mod permissions { - use test_util as util; - use util::TestContext; - - // TODO(bartlomieju): remove --unstable once Deno.Command 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-{permission}")) - .arg("run/permission_test.ts") - .arg(format!("{permission}Required")) - .spawn() - .unwrap() - .wait() - .unwrap(); - assert!(status.success()); - } - } - - // TODO(bartlomieju): remove --unstable once Deno.Command 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 {permission}Required"), - 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() - )) - .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(), - util::root_path().join("Cargo.toml"), - ), - 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(), - )) - .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(); - let js_dir = util::root_path().join("js"); - 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"), - ), - 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(); - let js_dir = util::root_path().join("js"); - for permission in &PERMISSION_VARIANTS { - let status = util::deno_cmd() - .current_dir(&util::testdata_path()) - .arg("run") - .arg(format!("--allow-{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-{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-{permission}=tls/../")) - .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 collide 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() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/061_permissions_request.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "┌ ⚠️ Deno requests read access to \"foo\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("y"); - console.expect(concat!( - "┌ ⚠️ Deno requests read access to \"bar\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("n"); - console.expect("granted"); - console.expect("prompt"); - console.expect("denied"); - }); - } - - #[test] - fn _061_permissions_request_sync() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/061_permissions_request_sync.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "┌ ⚠️ Deno requests read access to \"foo\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("y"); - console.expect(concat!( - "┌ ⚠️ Deno requests read access to \"bar\".\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("n"); - console.expect("granted"); - console.expect("prompt"); - console.expect("denied"); - }); - } - - #[test] - fn _062_permissions_request_global() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/062_permissions_request_global.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "┌ ⚠️ Deno requests read access.\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("y\n"); - console - .expect("PermissionStatus { state: \"granted\", onchange: null }"); - console - .expect("PermissionStatus { state: \"granted\", onchange: null }"); - console - .expect("PermissionStatus { state: \"granted\", onchange: null }"); - }); - } - - #[test] - fn _062_permissions_request_global_sync() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "run/062_permissions_request_global_sync.ts"]) - .with_pty(|mut console| { - console.expect(concat!( - "┌ ⚠️ Deno requests read access.\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-read to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)", - )); - console.write_line_raw("y"); - console - .expect("PermissionStatus { state: \"granted\", onchange: null }"); - console - .expect("PermissionStatus { state: \"granted\", onchange: null }"); - console - .expect("PermissionStatus { state: \"granted\", onchange: null }"); - }); - } - - itest!(_063_permissions_revoke { - args: "run --allow-read=foo,bar run/063_permissions_revoke.ts", - output: "run/063_permissions_revoke.ts.out", - }); - - itest!(_063_permissions_revoke_sync { - args: "run --allow-read=foo,bar run/063_permissions_revoke_sync.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", - }); - - itest!(_064_permissions_revoke_global_sync { - args: "run --allow-read=foo,bar run/064_permissions_revoke_global_sync.ts", - output: "run/064_permissions_revoke_global.ts.out", - }); - - itest!(_065_permissions_revoke_net { - args: "run --allow-net run/065_permissions_revoke_net.ts", - output: "run/065_permissions_revoke_net.ts.out", - }); - - #[test] - fn _066_prompt() { - TestContext::default() - .new_command() - .args_vec(["run", "--quiet", "--unstable", "run/066_prompt.ts"]) - .with_pty(|mut console| { - console.expect("What is your name? Jane Doe"); - console.write_line_raw(""); - console.expect("Your name is Jane Doe."); - - console.expect("Prompt "); - console.write_line_raw("foo"); - console.expect("Your input is foo."); - console.expect("Question 0 [y/N] "); - console.write_line_raw("Y"); - console.expect("Your answer is true"); - console.expect("Question 1 [y/N] "); - console.write_line_raw("N"); - console.expect("Your answer is false"); - console.expect("Question 2 [y/N] "); - console.write_line_raw("yes"); - console.expect("Your answer is false"); - console.expect("Confirm [y/N] "); - console.write_line(""); - console.expect("Your answer is false"); - console.expect("What is Windows EOL? "); - console.write_line("windows"); - console.expect("Your answer is \"windows\""); - console.expect("Hi [Enter] "); - console.write_line(""); - console.expect("Alert [Enter] "); - console.write_line(""); - console.expect("The end of test"); - }); - } - - itest!(dynamic_import_static_analysis_no_permissions { - args: "run --quiet --reload --no-prompt dynamic_import/static_analysis_no_permissions.ts", - output: "dynamic_import/static_analysis_no_permissions.ts.out", - }); - - 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 --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() { - TestContext::default() - .new_command() - .args_vec(["run", "run/issue9750.js"]) - .with_pty(|mut console| { - console.expect("Enter 'yy':"); - console.write_line_raw("yy"); - console.expect(concat!( - "┌ ⚠️ Deno requests env access.\r\n", - "├ Requested by `Deno.permissions.request()` API.\r\n", - "├ Run again with --allow-env to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", - )); - console.write_line_raw("n"); - console.expect("Denied env access."); - console.expect(concat!( - "┌ ⚠️ Deno requests env access to \"SECRET\".\r\n", - "├ Run again with --allow-env to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", - )); - console.write_line_raw("n"); - console.expect_all(&[ - "Denied env access to \"SECRET\".", - "PermissionDenied: Requires env access to \"SECRET\", run again with the --allow-env flag", - ]); - }); -} - -// 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"); - mod1_path.write(""); - let status = util::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()); - mod1_path.write("export { foo } from \"./mod2.ts\";"); - mod2_path.write("("); - let status = util::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"); - // With a fresh `DENO_DIR`, run a module with a dependency and a type error. - mod1_path.write("import './mod2.ts'; Deno.exit('0');"); - mod2_path.write("console.log('Hello, world!');"); - let status = util::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 = util::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_attributes_static_import { - args: "run --allow-read import_attributes/static_import.ts", - output: "import_attributes/static_import.out", -}); - -itest!(import_attributes_static_export { - args: "run --allow-read import_attributes/static_export.ts", - output: "import_attributes/static_export.out", -}); - -itest!(import_attributes_static_error { - args: "run --allow-read import_attributes/static_error.ts", - output: "import_attributes/static_error.out", - exit_code: 1, -}); - -itest!(import_attributes_dynamic_import { - args: "run --allow-read --check import_attributes/dynamic_import.ts", - output: "import_attributes/dynamic_import.out", -}); - -itest!(import_attributes_dynamic_error { - args: "run --allow-read import_attributes/dynamic_error.ts", - output: "import_attributes/dynamic_error.out", - exit_code: 1, -}); - -itest!(import_attributes_type_check { - args: "run --allow-read --check import_attributes/type_check.ts", - output: "import_attributes/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!(config_auto_discovered_for_local_script_log { - args: "run -L debug run/with_config/frontend_work.ts", - output: "run/with_config/auto_discovery_log.out", -}); - -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!(package_json_auto_discovered_for_local_script_arg { - args: "run -L debug -A no_deno_json/main.ts", - output: "run/with_package_json/no_deno_json/main.out", - // notice this is not in no_deno_json - cwd: Some("run/with_package_json/"), - // prevent creating a node_modules dir in the code directory - copy_temp_dir: Some("run/with_package_json/"), - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -// In this case we shouldn't discover `package.json` file, because it's in a -// directory that is above the directory containing `deno.json` file. -itest!( - package_json_auto_discovered_for_local_script_arg_with_stop { - args: "run -L debug with_stop/some/nested/dir/main.ts", - output: "run/with_package_json/with_stop/main.out", - cwd: Some("run/with_package_json/"), - copy_temp_dir: Some("run/with_package_json/"), - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, - } -); - -itest!(package_json_not_auto_discovered_no_config { - args: "run -L debug -A --no-config noconfig.ts", - output: "run/with_package_json/no_deno_json/noconfig.out", - cwd: Some("run/with_package_json/no_deno_json/"), -}); - -itest!(package_json_not_auto_discovered_no_npm { - args: "run -L debug -A --no-npm noconfig.ts", - output: "run/with_package_json/no_deno_json/noconfig.out", - cwd: Some("run/with_package_json/no_deno_json/"), -}); - -itest!(package_json_not_auto_discovered_env_var { - args: "run -L debug -A noconfig.ts", - output: "run/with_package_json/no_deno_json/noconfig.out", - cwd: Some("run/with_package_json/no_deno_json/"), - envs: vec![("DENO_NO_PACKAGE_JSON".to_string(), "1".to_string())], -}); - -itest!( - package_json_auto_discovered_node_modules_relative_package_json { - args: "run -A main.js", - output: "run/with_package_json/no_deno_json/sub_dir/main.out", - cwd: Some("run/with_package_json/no_deno_json/sub_dir"), - copy_temp_dir: Some("run/with_package_json/no_deno_json/"), - envs: env_vars_for_npm_tests(), - http_server: true, - } -); - -itest!(package_json_auto_discovered_for_npm_binary { - args: "run -L debug -A npm:@denotest/bin/cli-esm this is a test", - output: "run/with_package_json/npm_binary/main.out", - cwd: Some("run/with_package_json/npm_binary/"), - copy_temp_dir: Some("run/with_package_json/"), - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(package_json_auto_discovered_no_package_json_imports { - // this should not use --quiet because we should ensure no package.json install occurs - args: "run -A no_package_json_imports.ts", - output: "run/with_package_json/no_deno_json/no_package_json_imports.out", - cwd: Some("run/with_package_json/no_deno_json"), - copy_temp_dir: Some("run/with_package_json/no_deno_json"), -}); - -#[test] -fn package_json_with_deno_json() { - let context = TestContextBuilder::for_npm() - .use_copy_temp_dir("package_json/deno_json/") - .cwd("package_json/deno_json/") - .build(); - let output = context.new_command().args("run --quiet -A main.ts").run(); - output.assert_matches_file("package_json/deno_json/main.out"); - - assert!(context - .temp_dir() - .path() - .join("package_json/deno_json/deno.lock") - .exists()); - - // run again and ensure the top level install doesn't happen twice - let output = context - .new_command() - .args("run --log-level=debug -A main.ts") - .run(); - let output = output.combined_output(); - assert_contains!(output, "Skipping top level install."); -} - -#[test] -fn package_json_error_dep_value_test() { - let context = TestContextBuilder::for_npm() - .use_copy_temp_dir("package_json/invalid_value") - .cwd("package_json/invalid_value") - .build(); - - // should run fine when not referencing a failing dep entry - context - .new_command() - .args("run ok.ts") - .run() - .assert_matches_file("package_json/invalid_value/ok.ts.out"); - - // should fail when referencing a failing dep entry - context - .new_command() - .args("run error.ts") - .run() - .assert_exit_code(1) - .assert_matches_file("package_json/invalid_value/error.ts.out"); - - // should output a warning about the failing dep entry - context - .new_command() - .args("task test") - .run() - .assert_matches_file("package_json/invalid_value/task.out"); -} - -#[test] -fn package_json_no_node_modules_dir_created() { - // it should not create a node_modules directory - let context = TestContextBuilder::new() - .add_npm_env_vars() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - - temp_dir.write("deno.json", "{}"); - temp_dir.write("package.json", "{}"); - temp_dir.write("main.ts", ""); - - context.new_command().args("run main.ts").run(); - - assert!(!temp_dir.path().join("node_modules").exists()); -} - -#[test] -fn node_modules_dir_no_npm_specifiers_no_dir_created() { - // it should not create a node_modules directory - let context = TestContextBuilder::new() - .add_npm_env_vars() - .use_temp_cwd() - .build(); - let temp_dir = context.temp_dir(); - - temp_dir.write("deno.json", "{}"); - temp_dir.write("main.ts", ""); - - context - .new_command() - .args("run --node-modules-dir main.ts") - .run(); - - assert!(!temp_dir.path().join("node_modules").exists()); -} - -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!(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 -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_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 --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 -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 context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - let files = vec!["file.d.ts", "file.d.cts", "file.d.mts"]; - - for file in files { - temp_dir.write(file, ""); - context - .new_command() - .args_vec(["run", file]) - .run() - .skip_output_check() - .assert_exit_code(0); - } -} - -itest!(test_and_bench_are_noops_in_run { - args: "run run/test_and_bench_in_run.js", - output_str: Some(""), -}); - -#[cfg(not(target_os = "windows"))] -itest!(spawn_kill_permissions { - args: "run --quiet --allow-run=cat spawn_kill_permissions.ts", - output_str: Some(""), -}); - -itest!(followup_dyn_import_resolved { - args: "run --allow-read run/followup_dyn_import_resolves/main.ts", - output: "run/followup_dyn_import_resolves/main.ts.out", -}); - -itest!(allow_run_allowlist_resolution { - args: "run --quiet -A allow_run_allowlist_resolution.ts", - output: "allow_run_allowlist_resolution.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!(rejection_handled { - args: "run --check run/rejection_handled.ts", - output: "run/rejection_handled.out", -}); - -itest!(nested_error { - args: "run run/nested_error/main.ts", - output: "run/nested_error/main.ts.out", - exit_code: 1, -}); - -itest!(node_env_var_allowlist { - args: "run --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"); - fixture_path.write("console.log(\"42\");"); - let output = Command::new(util::deno_exe_path()) - .env("DENO_DIR", deno_dir.path()) - .current_dir(util::testdata_path()) - .arg("run") - .arg(&fixture_path) - .output() - .expect("Failed to spawn script"); - assert!(output.status.success()); - let actual = std::str::from_utf8(&output.stdout).unwrap(); - assert_eq!(actual, "42\n"); - fixture_path.write("console.log(\"43\");"); - let output = Command::new(util::deno_exe_path()) - .env("DENO_DIR", deno_dir.path()) - .current_dir(util::testdata_path()) - .arg("run") - .arg(fixture_path) - .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"); - fixture_path.write("console.log(\"42\");"); - 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) - .output() - .expect("Failed to spawn script"); - assert!(output.status.success()); - let actual = std::str::from_utf8(&output.stdout).unwrap(); - assert_eq!(actual, "42\n"); - fixture_path.write("console.log(\"43\");"); - 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) - .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") - .piped_output() - .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") - .piped_output() - .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") - .piped_output() - .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") - .piped_output() - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - let err = String::from_utf8_lossy(&output.stderr); - let out = String::from_utf8_lossy(&output.stdout); - if !output.status.success() { - eprintln!("stderr: {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); - } - - // 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") - .piped_output() - .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 (in promise) 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") - .piped_output() - .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 (in promise) PermissionDenied: Requires net access to "127.0.0.1:4553""#)); - assert!(out.is_empty()); - } - - handle.abort(); -} - -#[tokio::test] -async fn http2_request_url() { - 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_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(); -} - -#[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_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_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 (in promise) 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, -}); - -itest!(default_file_extension_is_js { - args: "run --check file_extensions/js_without_extension", - output: "file_extensions/js_without_extension.out", - exit_code: 0, -}); - -itest!(js_without_extension { - args: "run --ext js --check file_extensions/js_without_extension", - output: "file_extensions/js_without_extension.out", - exit_code: 0, -}); - -itest!(ts_without_extension { - args: "run --ext ts --check file_extensions/ts_without_extension", - output: "file_extensions/ts_without_extension.out", - exit_code: 0, -}); - -itest!(ext_flag_takes_precedence_over_extension { - args: "run --ext ts --check file_extensions/ts_with_js_extension.js", - output: "file_extensions/ts_with_js_extension.out", - exit_code: 0, -}); - -#[tokio::test(flavor = "multi_thread")] -async fn websocketstream_ping() { - 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 srv_fn = hyper::service::service_fn(|mut req| async move { - let (response, upgrade_fut) = - fastwebsockets::upgrade::upgrade(&mut req).unwrap(); - tokio::spawn(async move { - let mut ws = upgrade_fut.await.unwrap(); - - ws.write_frame(fastwebsockets::Frame::text(b"A"[..].into())) - .await - .unwrap(); - ws.write_frame(fastwebsockets::Frame::new( - true, - fastwebsockets::OpCode::Ping, - None, - vec![].into(), - )) - .await - .unwrap(); - ws.write_frame(fastwebsockets::Frame::text(b"B"[..].into())) - .await - .unwrap(); - let message = ws.read_frame().await.unwrap(); - assert_eq!(message.opcode, fastwebsockets::OpCode::Pong); - ws.write_frame(fastwebsockets::Frame::text(b"C"[..].into())) - .await - .unwrap(); - ws.write_frame(fastwebsockets::Frame::close_raw(vec![].into())) - .await - .unwrap(); - }); - Ok::<_, std::convert::Infallible>(response) - }); - - let child = util::deno_cmd() - .arg("test") - .arg("--unstable") - .arg("--allow-net") - .arg("--cert") - .arg(root_ca) - .arg(script) - .stdout_piped() - .spawn() - .unwrap(); - let server = tokio::net::TcpListener::bind("127.0.0.1:4513") - .await - .unwrap(); - tokio::spawn(async move { - let (stream, _) = server.accept().await.unwrap(); - let io = hyper_util::rt::TokioIo::new(stream); - let conn_fut = hyper::server::conn::http1::Builder::new() - .serve_connection(io, srv_fn) - .with_upgrades(); - - if let Err(e) = conn_fut.await { - eprintln!("websocket server error: {e:?}"); - } - }); - - let r = child.wait_with_output().unwrap(); - assert!(r.status.success()); -} - -struct SpawnExecutor; - -impl<Fut> hyper::rt::Executor<Fut> for SpawnExecutor -where - Fut: std::future::Future + Send + 'static, - Fut::Output: Send + 'static, -{ - fn execute(&self, fut: Fut) { - deno_core::unsync::spawn(fut); - } -} - -#[tokio::test] -async 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_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 stream = tokio::net::TcpStream::connect("localhost:4319") - .await - .unwrap(); - let req = http::Request::builder() - .header(http::header::UPGRADE, "websocket") - .header(http::header::CONNECTION, "keep-alive, Upgrade") - .header( - "Sec-WebSocket-Key", - fastwebsockets::handshake::generate_key(), - ) - .header("Sec-WebSocket-Version", "13") - .uri("ws://localhost:4319") - .body(http_body_util::Empty::<Bytes>::new()) - .unwrap(); - - let (mut socket, _) = - fastwebsockets::handshake::client(&SpawnExecutor, req, stream) - .await - .unwrap(); - - let message = socket.read_frame().await.unwrap(); - assert_eq!(message.opcode, fastwebsockets::OpCode::Close); - assert!(message.payload.is_empty()); - socket - .write_frame(fastwebsockets::Frame::close_raw(vec![].into())) - .await - .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 -#[tokio::test] -async 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_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 stream = tokio::net::TcpStream::connect("localhost:4509") - .await - .unwrap(); - let req = http::Request::builder() - .header(http::header::UPGRADE, "websocket") - .header(http::header::CONNECTION, "keep-alive, Upgrade") - .header( - "Sec-WebSocket-Key", - fastwebsockets::handshake::generate_key(), - ) - .header("Sec-WebSocket-Version", "13") - .uri("ws://localhost:4509") - .body(http_body_util::Empty::<Bytes>::new()) - .unwrap(); - - let (_socket, _) = - fastwebsockets::handshake::client(&SpawnExecutor, req, stream) - .await - .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, -}); - -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 context = TestContext::with_http_server(); - context - .new_command() - .args("repl --quiet") - .with_pty(|mut console| { - console.write_line( - "const a = await import('http://localhost:4545/run/019_media_types.ts');", - ); - console.expect("Allow?"); - console.write_line_raw("y"); - console.expect_all(&["success", "true"]); - }); -} - -#[test] -fn stdio_streams_are_locked_in_permission_prompt() { - if !util::pty::Pty::is_supported() { - // Don't deal with the logic below if the with_pty - // block doesn't even run (ex. on Windows CI) - return; - } - - let context = TestContextBuilder::new() - .use_http_server() - .use_copy_temp_dir("run/stdio_streams_are_locked_in_permission_prompt") - .build(); - let mut passed_test = false; - let mut i = 0; - while !passed_test { - i += 1; - if i > 5 { - panic!("Output happened before permission prompt too many times"); - } - - context - .new_command() - .args("repl --allow-read") - .with_pty(|mut console| { - let malicious_output = r#"Are you sure you want to continue?"#; - - console.write_line(r#"const url = "file://" + Deno.cwd().replace("\\", "/") + "/run/stdio_streams_are_locked_in_permission_prompt/worker.js";"#); - console.expect("undefined"); - // ensure this file exists - console.write_line(r#"const _file = Deno.readTextFileSync("./run/stdio_streams_are_locked_in_permission_prompt/worker.js");"#); - console.expect("undefined"); - console.write_line(r#"new Worker(url, { type: "module" }); await Deno.writeTextFile("./text.txt", "some code");"#); - console.expect("Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all write permissions)"); - - // Due to the main thread being slow, it may occur that the worker thread outputs - // before the permission prompt is shown. This is not a bug and just a timing issue - // when dealing with multiple threads. If this occurs, detect such a case and then - // retry running the test. - if let Some(malicious_index) = console.all_output().find(malicious_output) { - let prompt_index = console.all_output().find("Allow?").unwrap(); - // Ensure the malicious output is shown before the prompt as we - // expect in this scenario. If not, that would indicate a bug. - assert!(malicious_index < prompt_index); - return; - } - - std::thread::sleep(Duration::from_millis(50)); // give the other thread some time to output - console.write_line_raw("invalid"); - console.expect("Unrecognized option."); - console.expect("Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all write permissions)"); - console.write_line_raw("y"); - console.expect("Granted write access to"); - - // this output should now be shown below and not above - console.expect(malicious_output); - passed_test = true; - }); - } -} - -#[test] -fn permission_prompt_strips_ansi_codes_and_control_chars() { - util::with_pty(&["repl"], |mut console| { - console.write_line( - r#"Deno.permissions.request({ name: "env", variable: "\rDo you like ice cream? y/n" });"# - ); - // will be uppercase on windows - let env_name = if cfg!(windows) { - "DO YOU LIKE ICE CREAM? Y/N" - } else { - "Do you like ice cream? y/n" - }; - console.expect(format!( - "┌ ⚠️ Deno requests env access to \"{}\".", - env_name - )) - }); - - util::with_pty(&["repl"], |mut console| { - console.write_line_raw(r#"const boldANSI = "\u001b[1m";"#); - console.expect("undefined"); - console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#); - console.expect("undefined"); - console.write_line_raw(r#"const prompt = `┌ ⚠️ ${boldANSI}Deno requests run access to "echo"${unboldANSI}\n ├ Requested by \`Deno.Command().output()`"#); - console.expect("undefined"); - console.write_line_raw(r#"const moveANSIUp = "\u001b[1A";"#); - console.expect("undefined"); - console.write_line_raw(r#"const clearANSI = "\u001b[2K";"#); - console.expect("undefined"); - console.write_line_raw(r#"const moveANSIStart = "\u001b[1000D";"#); - console.expect("undefined"); - }); -} - -itest!(node_builtin_modules_ts { - args: "run --quiet --allow-read run/node_builtin_modules/mod.ts hello there", - output: "run/node_builtin_modules/mod.ts.out", - envs: env_vars_for_npm_tests(), - exit_code: 0, -}); - -itest!(node_builtin_modules_js { - args: "run --quiet --allow-read run/node_builtin_modules/mod.js hello there", - output: "run/node_builtin_modules/mod.js.out", - envs: env_vars_for_npm_tests(), - exit_code: 0, -}); - -itest!(node_prefix_missing { - args: "run --quiet run/node_prefix_missing/main.ts", - output: "run/node_prefix_missing/main.ts.out", - envs: env_vars_for_npm_tests(), - exit_code: 1, -}); - -itest!(node_prefix_missing_unstable_bare_node_builtins_enbaled { - args: "run --unstable-bare-node-builtins run/node_prefix_missing/main.ts", - output: "run/node_prefix_missing/main.ts.out_feature_enabled", - envs: env_vars_for_npm_tests(), - exit_code: 0, -}); - -itest!( - node_prefix_missing_unstable_bare_node_builtins_enbaled_by_env { - args: "run run/node_prefix_missing/main.ts", - output: "run/node_prefix_missing/main.ts.out_feature_enabled", - envs: [ - env_vars_for_npm_tests(), - vec![( - "DENO_UNSTABLE_BARE_NODE_BUILTINS".to_string(), - "1".to_string() - )] - ] - .concat(), - exit_code: 0, - } -); - -itest!(node_prefix_missing_unstable_bare_node_builtins_enbaled_by_config { - args: "run --config=run/node_prefix_missing/config.json run/node_prefix_missing/main.ts", - output: "run/node_prefix_missing/main.ts.out_feature_enabled", - envs: env_vars_for_npm_tests(), - exit_code: 0, -}); - -itest!(node_prefix_missing_unstable_bare_node_builtins_enbaled_with_import_map { - args: "run --unstable-bare-node-builtins --import-map run/node_prefix_missing/import_map.json run/node_prefix_missing/main.ts", - output: "run/node_prefix_missing/main.ts.out_feature_enabled", - envs: env_vars_for_npm_tests(), - exit_code: 0, -}); - -itest!(dynamic_import_syntax_error { - args: "run -A run/dynamic_import_syntax_error.js", - output: "run/dynamic_import_syntax_error.js.out", - exit_code: 1, -}); - -itest!(extension_import { - args: "run run/extension_import.ts", - output: "run/extension_import.ts.out", - exit_code: 1, -}); - -itest!(extension_dynamic_import { - args: "run run/extension_dynamic_import.ts", - output: "run/extension_dynamic_import.ts.out", - exit_code: 1, -}); - -#[test] -pub fn vendor_dir_config_file() { - let test_context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .build(); - let temp_dir = test_context.temp_dir(); - let vendor_dir = temp_dir.path().join("vendor"); - let rm_vendor_dir = || std::fs::remove_dir_all(&vendor_dir).unwrap(); - - temp_dir.write("deno.json", r#"{ "vendor": true }"#); - temp_dir.write( - "main.ts", - r#"import { returnsHi } from 'http://localhost:4545/subdir/mod1.ts'; -console.log(returnsHi());"#, - ); - - let deno_run_cmd = test_context.new_command().args("run --quiet main.ts"); - deno_run_cmd.run().assert_matches_text("Hi\n"); - - assert!(vendor_dir.exists()); - rm_vendor_dir(); - temp_dir.write("deno.json", r#"{ "vendor": false }"#); - - deno_run_cmd.run().assert_matches_text("Hi\n"); - assert!(!vendor_dir.exists()); - test_context - .new_command() - .args("cache --quiet --vendor main.ts") - .run(); - assert!(vendor_dir.exists()); - rm_vendor_dir(); - - temp_dir.write("deno.json", r#"{ "vendor": true }"#); - let cache_command = test_context.new_command().args("cache --quiet main.ts"); - cache_command.run(); - - assert!(vendor_dir.exists()); - let mod1_file = vendor_dir - .join("http_localhost_4545") - .join("subdir") - .join("mod1.ts"); - mod1_file.write("export function returnsHi() { return 'bye bye bye'; }"); - - // won't match the lockfile now - deno_run_cmd - .run() - .assert_matches_text(r#"error: The source code is invalid, as it does not match the expected hash in the lock file. - Specifier: http://localhost:4545/subdir/mod1.ts - Lock file: [WILDCARD]deno.lock -"#) - .assert_exit_code(10); - - // try updating by deleting the lockfile - let lockfile = temp_dir.path().join("deno.lock"); - lockfile.remove_file(); - cache_command.run(); - - // now it should run - deno_run_cmd.run().assert_matches_text("bye bye bye\n"); - assert!(lockfile.exists()); - - // ensure we can add and execute files in directories that have a hash in them - test_context - .new_command() - // http_localhost_4545/subdir/#capitals_c75d7/main.js - .args("cache http://localhost:4545/subdir/CAPITALS/main.js") - .run() - .skip_output_check(); - assert_eq!( - vendor_dir.join("manifest.json").read_json_value(), - json!({ - "folders": { - "http://localhost:4545/subdir/CAPITALS/": "http_localhost_4545/subdir/#capitals_c75d7" - } - }) - ); - vendor_dir - .join("http_localhost_4545/subdir/#capitals_c75d7/hello_there.ts") - .write("console.log('hello there');"); - test_context - .new_command() - // todo(dsherret): seems wrong that we don't auto-discover the config file to get the vendor directory for this - .args("run --vendor http://localhost:4545/subdir/CAPITALS/hello_there.ts") - .run() - .assert_matches_text("hello there\n"); - - // now try importing directly from the vendor folder - temp_dir.write( - "main.ts", - r#"import { returnsHi } from './vendor/http_localhost_4545/subdir/mod1.ts'; -console.log(returnsHi());"#, - ); - deno_run_cmd - .run() - .assert_matches_text("error: Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring. - at [WILDCARD]/main.ts:1:27 -") - .assert_exit_code(1); -} - -itest!(explicit_resource_management { - args: "run --quiet --check run/explicit_resource_management/main.ts", - output: "run/explicit_resource_management/main.out", -}); - -itest!(workspaces_basic { - args: "run -L debug -A main.ts", - output: "run/workspaces/basic/main.out", - cwd: Some("run/workspaces/basic/"), - copy_temp_dir: Some("run/workspaces/basic/"), - envs: env_vars_for_npm_tests(), - http_server: true, -}); - -itest!(workspaces_member_outside_root_dir { - args: "run -A main.ts", - output: "run/workspaces/member_outside_root_dir/main.out", - cwd: Some("run/workspaces/member_outside_root_dir/"), - copy_temp_dir: Some("run/workspaces/member_outside_root_dir/"), - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(workspaces_nested_member { - args: "run -A main.ts", - output: "run/workspaces/nested_member/main.out", - cwd: Some("run/workspaces/nested_member/"), - copy_temp_dir: Some("run/workspaces/nested_member/"), - envs: env_vars_for_npm_tests(), - http_server: true, - exit_code: 1, -}); - -itest!(unsafe_proto { - args: "run -A run/unsafe_proto/main.js", - output: "run/unsafe_proto/main.out", - http_server: false, - exit_code: 0, -}); - -itest!(unsafe_proto_flag { - args: "run -A --unstable-unsafe-proto run/unsafe_proto/main.js", - output: "run/unsafe_proto/main_with_unsafe_proto_flag.out", - http_server: false, - exit_code: 0, -}); - -#[test] -fn test_unstable_sloppy_imports() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("a.ts", "export class A {}"); - temp_dir.write("b.js", "export class B {}"); - temp_dir.write("c.mts", "export class C {}"); - temp_dir.write("d.mjs", "export class D {}"); - temp_dir.write("e.tsx", "export class E {}"); - temp_dir.write("f.jsx", "export class F {}"); - let dir = temp_dir.path().join("dir"); - dir.create_dir_all(); - dir.join("index.tsx").write("export class G {}"); - temp_dir.write( - "main.ts", - r#"import * as a from "./a.js"; -import * as b from "./b"; -import * as c from "./c"; -import * as d from "./d"; -import * as e from "./e"; -import * as e2 from "./e.js"; -import * as f from "./f"; -import * as g from "./dir"; -console.log(a.A); -console.log(b.B); -console.log(c.C); -console.log(d.D); -console.log(e.E); -console.log(e2.E); -console.log(f.F); -console.log(g.G); -"#, - ); - - // run without sloppy imports - context - .new_command() - .args("run main.ts") - .run() - .assert_matches_text(r#"error: Module not found "file:///[WILDCARD]/a.js". Maybe change the extension to '.ts' or run with --unstable-sloppy-imports - at file:///[WILDCARD]/main.ts:1:20 -"#) - .assert_exit_code(1); - - // now run with sloppy imports - temp_dir.write("deno.json", r#"{ "unstable": ["sloppy-imports"] }"#); - context - .new_command() - .args("run main.ts") - .run() - .assert_matches_text( - "Warning Sloppy imports are not recommended and have a negative impact on performance. -Warning Sloppy module resolution (hint: update .js extension to .ts) - at file:///[WILDCARD]/main.ts:1:20 -Warning Sloppy module resolution (hint: add .js extension) - at file:///[WILDCARD]/main.ts:2:20 -Warning Sloppy module resolution (hint: add .mts extension) - at file:///[WILDCARD]/main.ts:3:20 -Warning Sloppy module resolution (hint: add .mjs extension) - at file:///[WILDCARD]/main.ts:4:20 -Warning Sloppy module resolution (hint: add .tsx extension) - at file:///[WILDCARD]/main.ts:5:20 -Warning Sloppy module resolution (hint: update .js extension to .tsx) - at file:///[WILDCARD]/main.ts:6:21 -Warning Sloppy module resolution (hint: add .jsx extension) - at file:///[WILDCARD]/main.ts:7:20 -Warning Sloppy module resolution (hint: specify path to index.tsx file in directory instead) - at file:///[WILDCARD]/main.ts:8:20 -[class A] -[class B] -[class C] -[class D] -[class E] -[class E] -[class F] -[class G] -", - ); -} - -itest!(unstable_temporal_api { - args: "run --unstable-temporal --check run/unstable_temporal_api/main.ts", - output: "run/unstable_temporal_api/main.out", - http_server: false, - exit_code: 0, -}); - -itest!(unstable_temporal_api_missing_flag { - args: "run run/unstable_temporal_api/missing_flag.js", - output: "run/unstable_temporal_api/missing_flag.out", - http_server: false, - exit_code: 1, -}); - -// TODO(bartlomieju): temporary disabled -// itest!(warn_on_deprecated_api { -// args: "run -A run/warn_on_deprecated_api/main.js", -// output: "run/warn_on_deprecated_api/main.out", -// http_server: true, -// exit_code: 0, -// }); - -// itest!(warn_on_deprecated_api_verbose { -// args: "run -A run/warn_on_deprecated_api/main.js", -// output: "run/warn_on_deprecated_api/main.verbose.out", -// envs: vec![("DENO_VERBOSE_WARNINGS".to_string(), "1".to_string())], -// http_server: true, -// exit_code: 0, -// }); - -// itest!(warn_on_deprecated_api_with_flag { -// args: "run -A --quiet run/warn_on_deprecated_api/main.js", -// output: "run/warn_on_deprecated_api/main_disabled_flag.out", -// http_server: true, -// exit_code: 0, -// }); - -// itest!(warn_on_deprecated_api_with_env_var { -// args: "run -A run/warn_on_deprecated_api/main.js", -// envs: vec![("DENO_NO_DEPRECATION_WARNINGS".to_string(), "1".to_string())], -// output: "run/warn_on_deprecated_api/main_disabled_env.out", -// http_server: true, -// exit_code: 0, -// }); - -#[test] -fn deno_json_imports_expand() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let dir = test_context.temp_dir(); - dir.write( - "deno.json", - r#"{ - "imports": { - "basic": "npm:@denotest/esm-basic" - } -}"#, - ); - - dir.write( - "main.ts", - r#" -// import map should resolve -import { setValue, getValue } from "basic"; -// this entry should have been added automatically -import { hello } from "basic/other.mjs"; - -setValue(5); -console.log(getValue()); -console.log(hello()); -"#, - ); - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text("[WILDCARD]5\nhello, world!\n"); -} - -#[test] -fn deno_json_imports_expand_doesnt_overwrite_existing_entries() { - let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let dir = test_context.temp_dir(); - dir.write( - "deno.json", - r#"{ - "imports": { - "basic": "npm:@denotest/esm-basic", - "basic/": "npm:/@denotest/sub-folders/folder_index_js/" - } -}"#, - ); - - dir.write( - "main.ts", - r#" -// import map should resolve -import { setValue, getValue } from "basic"; -// this entry should map to explicitly specified "basic/" mapping -import { add } from "basic/index.js"; - -setValue(5); -console.log(getValue()); -console.log(add(3, 4)); -"#, - ); - let output = test_context.new_command().args("run main.ts").run(); - output.assert_matches_text("[WILDCARD]5\n7\n"); -} diff --git a/cli/tests/integration/shared_library_tests.rs b/cli/tests/integration/shared_library_tests.rs deleted file mode 100644 index 4d33e6584..000000000 --- a/cli/tests/integration/shared_library_tests.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -#[cfg(all(target_os = "linux", target_arch = "x86_64"))] -#[test] -// https://github.com/denoland/deno/issues/18266 -fn linux_shared_libraries() { - use test_util as util; - - const EXPECTED: [&str; 7] = [ - "linux-vdso.so.1", - "libdl.so.2", - "libgcc_s.so.1", - "libpthread.so.0", - "libm.so.6", - "libc.so.6", - "/lib64/ld-linux-x86-64.so.2", - ]; - - let ldd = std::process::Command::new("ldd") - .arg("-L") - .arg(util::deno_exe_path()) - .output() - .expect("Failed to execute ldd"); - - let output = std::str::from_utf8(&ldd.stdout).unwrap(); - // Ensure that the output contains only the expected shared libraries. - for line in output.lines().skip(1) { - let path = line.split_whitespace().next().unwrap(); - assert!( - EXPECTED.contains(&path), - "Unexpected shared library: {}", - path - ); - } -} - -#[cfg(target_os = "macos")] -#[test] -// https://github.com/denoland/deno/issues/18243 -// This test is to prevent inadvertently linking to more shared system libraries that usually -// increases dyld startup time. -fn macos_shared_libraries() { - use test_util as util; - - // target/release/deno: - // /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1953.1.0) - // /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 1228.0.0) - // /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore (compatibility version 1.2.0, current version 1.11.0, weak) - // /System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 341.16.0, weak) - // /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics (compatibility version 64.0.0, current version 1774.0.4, weak) - // /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders (compatibility version 1.0.0, current version 127.0.19, weak) - // /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0) - // /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0) - // /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0) - - // path and whether its weak or not - const EXPECTED: [(&str, bool); 9] = [ - ("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", false), - ("/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices", false), - ("/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", true), - ("/System/Library/Frameworks/Metal.framework/Versions/A/Metal", true), - ("/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics", true), - ("/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders", true), - ("/usr/lib/libiconv.2.dylib", false), - ("/usr/lib/libSystem.B.dylib", false), - ("/usr/lib/libobjc.A.dylib", false), - ]; - - let otool = std::process::Command::new("otool") - .arg("-L") - .arg(util::deno_exe_path()) - .output() - .expect("Failed to execute otool"); - - let output = std::str::from_utf8(&otool.stdout).unwrap(); - // Ensure that the output contains only the expected shared libraries. - for line in output.lines().skip(1) { - let (path, attributes) = line.trim().split_once(' ').unwrap(); - assert!( - EXPECTED.contains(&(path, attributes.ends_with("weak)"))), - "Unexpected shared library: {}", - path - ); - } -} diff --git a/cli/tests/integration/task_tests.rs b/cli/tests/integration/task_tests.rs deleted file mode 100644 index c8531c13f..000000000 --- a/cli/tests/integration/task_tests.rs +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2018-2024 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. - -use deno_core::serde_json::json; -use test_util::env_vars_for_npm_tests; -use test_util::TestContext; -use test_util::TestContextBuilder; - -itest!(task_no_args { - args: "task -q --config task/deno_json/deno.json", - output: "task/deno_json/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/deno.json --cwd .. echo_cwd", - output: "task/deno_json/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/deno.json --cwd .. echo_init_cwd", - output: "task/deno_json/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/deno.json echo_init_cwd", - output: "task/deno_json/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/deno_json", - output: "task/deno_json/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/deno.json non_existent", - output: "task/deno_json/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 - TestContext::default() - .new_command() - .args_vec(["task", "--config", "task/deno_json/deno.json", "echo_emoji"]) - .with_pty(|mut console| { - console.expect("Task echo_emoji echo 🔥\r\n🔥"); - }); -} - -itest!(task_boolean_logic { - args: "task -q --config task/deno_json/deno.json boolean_logic", - output: "task/deno_json/task_boolean_logic.out", - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -itest!(task_exit_code_5 { - args: "task --config task/deno_json/deno.json exit_code_5", - output: "task/deno_json/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/deno.json echo 2", - output: "task/deno_json/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/deno.json", - "echo", - "$(echo 5)" - ], - output: "task/deno_json/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/deno.json", - "echo", - "string \"quoted string\"" - ], - output: "task/deno_json/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/deno.json", - "echo", - "||", - "echo", - "5" - ], - output: "task/deno_json/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.json", - "deno_echo" - ], - output: "task/deno_json/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/deno.json", - "piped" - ], - output: "task/deno_json/task_piped_stdin.out", - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -itest!(task_package_json_no_arg { - args: "task", - cwd: Some("task/package_json/"), - output: "task/package_json/no_args.out", - envs: vec![("NO_COLOR".to_string(), "1".to_string())], - exit_code: 1, -}); - -itest!(task_package_json_echo { - args: "task --quiet echo", - cwd: Some("task/package_json/"), - output: "task/package_json/echo.out", - // use a temp dir because the node_modules folder will be created - copy_temp_dir: Some("task/package_json/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(task_package_json_npm_bin { - args: "task bin extra", - cwd: Some("task/package_json/"), - output: "task/package_json/bin.out", - copy_temp_dir: Some("task/package_json/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -// should not auto-install the packages in the package.json -// when using nodeModulesDir: false -itest!(task_package_json_node_modules_dir_false { - args: "task echo", - cwd: Some("task/package_json_node_modules_dir_false/"), - output: "task/package_json_node_modules_dir_false/bin.out", - copy_temp_dir: Some("task/package_json_node_modules_dir_false/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(task_both_no_arg { - args: "task", - cwd: Some("task/both/"), - output: "task/both/no_args.out", - envs: vec![("NO_COLOR".to_string(), "1".to_string())], - exit_code: 1, -}); - -itest!(task_both_deno_json_selected { - args: "task other", - cwd: Some("task/both/"), - output: "task/both/deno_selected.out", - copy_temp_dir: Some("task/both/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(task_both_package_json_selected { - args: "task bin asdf", - cwd: Some("task/both/"), - output: "task/both/package_json_selected.out", - copy_temp_dir: Some("task/both/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(task_both_prefers_deno { - args: "task output some text", - cwd: Some("task/both/"), - output: "task/both/prefers_deno.out", - copy_temp_dir: Some("task/both/"), - envs: env_vars_for_npm_tests(), - exit_code: 0, - http_server: true, -}); - -itest!(task_npx_non_existent { - args: "task non-existent", - cwd: Some("task/npx/"), - output: "task/npx/non_existent.out", - copy_temp_dir: Some("task/npx/"), - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); - -itest!(task_npx_on_own { - args: "task on-own", - cwd: Some("task/npx/"), - output: "task/npx/on_own.out", - copy_temp_dir: Some("task/npx/"), - envs: env_vars_for_npm_tests(), - exit_code: 1, - http_server: true, -}); - -itest!(task_pre_post { - args: "task test", - cwd: Some("task/package_json_pre_post/"), - output: "task/package_json_pre_post/bin.out", - copy_temp_dir: Some("task/package_json_pre_post/"), - exit_code: 0, - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -itest!(task_pre { - args: "task test", - cwd: Some("task/package_json_pre/"), - output: "task/package_json_pre/bin.out", - copy_temp_dir: Some("task/package_json_pre/"), - exit_code: 0, - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -itest!(task_post { - args: "task test", - cwd: Some("task/package_json_post/"), - output: "task/package_json_post/bin.out", - copy_temp_dir: Some("task/package_json_post/"), - exit_code: 0, - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -itest!(task_post_only { - args: "task test", - cwd: Some("task/package_json_post_only/"), - output: "task/package_json_post_only/bin.out", - copy_temp_dir: Some("task/package_json_post_only/"), - exit_code: 1, - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -itest!(task_pre_only { - args: "task test", - cwd: Some("task/package_json_pre_only/"), - output: "task/package_json_pre_only/bin.out", - copy_temp_dir: Some("task/package_json_pre_only/"), - exit_code: 1, - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -itest!(task_deno_no_pre_post { - args: "task test", - cwd: Some("task/deno_json_pre_post/"), - output: "task/deno_json_pre_post/bin.out", - copy_temp_dir: Some("task/deno_json_pre_post/"), - exit_code: 0, - envs: vec![("NO_COLOR".to_string(), "1".to_string())], -}); - -#[test] -fn task_byonm() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir().path(); - temp_dir.join("package.json").write_json(&json!({ - "name": "example", - "scripts": { - "say": "cowsay 'do make say'", - "think": "cowthink think" - }, - "dependencies": { - "cowsay": "*" - } - })); - temp_dir.join("deno.json").write_json(&json!({ - "unstable": ["byonm"], - })); - context.run_npm("install"); - - context - .new_command() - .args_vec(["task", "say"]) - .run() - .assert_matches_text( - r#"Task say cowsay 'do make say' - _____________ -< do make say > - ------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || -"#, - ); - - context - .new_command() - .args_vec(["task", "think"]) - .run() - .assert_matches_text( - r#"Task think cowthink think - _______ -( think ) - ------- - o ^__^ - o (oo)\_______ - (__)\ )\/\ - ||----w | - || || -"#, - ); -} diff --git a/cli/tests/integration/test_tests.rs b/cli/tests/integration/test_tests.rs deleted file mode 100644 index 27bef8007..000000000 --- a/cli/tests/integration/test_tests.rs +++ /dev/null @@ -1,671 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use deno_core::url::Url; -use test_util as util; -use util::assert_contains; -use util::assert_not_contains; -use util::env_vars_for_npm_tests; -use util::wildcard_match; -use util::TestContext; -use util::TestContextBuilder; - -#[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 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_deprecated_config { - args: "test --config test/collect/deno.deprecated.jsonc test/collect", - exit_code: 0, - output: "test/collect.deprecated.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!(test_filtered_out_only { - args: "test --quiet --filter foo test/filtered_out_only.ts", - output: "test/filtered_out_only.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 test/fail_fast_other.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 --no-run test/no_run.ts", - output: "test/no_run.out", - exit_code: 1, -}); - -itest!(allow_all { - args: "test --allow-all test/allow_all.ts", - exit_code: 0, - output: "test/allow_all.out", -}); - -itest!(allow_none { - args: "test test/allow_none.ts", - exit_code: 1, - output: "test/allow_none.out", -}); - -itest!(ops_sanitizer_unstable { - args: "test --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_closed_inside_started_before { - args: "test --trace-ops test/ops_sanitizer_closed_inside_started_before.ts", - exit_code: 1, - output: "test/ops_sanitizer_closed_inside_started_before.out", -}); - -itest!(ops_sanitizer_nexttick { - args: "test --no-check 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!(junit { - args: "test --reporter junit test/pass.ts", - output: "test/pass.junit.out", -}); - -#[test] -fn junit_path() { - let context = TestContextBuilder::new().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write("test.js", "Deno.test('does test', () => {});"); - let output = context - .new_command() - .args("test --junit-path=sub_dir/output.xml test.js") - .run(); - output.skip_output_check(); - output.assert_exit_code(0); - temp_dir - .path() - .join("sub_dir/output.xml") - .assert_matches_text("<?xml [WILDCARD]"); -} - -itest!(clear_timeout { - args: "test test/clear_timeout.ts", - exit_code: 0, - output: "test/clear_timeout.out", -}); - -itest!(hide_empty_suites { - args: "test --filter none test/pass.ts", - exit_code: 0, - output: "test/hide_empty_suites.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_dot_passing_steps { - args: "test --reporter=dot test/steps/passing_steps.ts", - exit_code: 0, - output: "test/steps/passing_steps.dot.out", -}); - -itest!(steps_dot_failing_steps { - args: "test --reporter=dot test/steps/failing_steps.ts", - exit_code: 1, - output: "test/steps/failing_steps.dot.out", -}); - -itest!(steps_dot_ignored_steps { - args: "test --reporter=dot test/steps/ignored_steps.ts", - exit_code: 0, - output: "test/steps/ignored_steps.dot.out", -}); - -itest!(steps_tap_passing_steps { - args: "test --reporter=tap test/steps/passing_steps.ts", - exit_code: 0, - output: "test/steps/passing_steps.tap.out", -}); - -itest!(steps_tap_failing_steps { - args: "test --reporter=tap test/steps/failing_steps.ts", - exit_code: 1, - envs: vec![("NO_COLOR".to_owned(), "1".to_owned())], - output: "test/steps/failing_steps.tap.out", -}); - -itest!(steps_tap_ignored_steps { - args: "test --reporter=tap test/steps/ignored_steps.ts", - exit_code: 0, - output: "test/steps/ignored_steps.tap.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", -}); - -itest!(before_unload_prevent_default { - args: "test --quiet test/before_unload_prevent_default.ts", - output: "test/before_unload_prevent_default.out", -}); - -#[test] -fn captured_output() { - let context = TestContext::default(); - let output = context - .new_command() - .args("test --allow-run --allow-read test/captured_output.ts") - .env("NO_COLOR", "1") - .run(); - - let output_start = "------- output -------"; - let output_end = "----- output end -----"; - output.assert_exit_code(0); - let output_text = output.combined_output(); - 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 context = TestContext::default(); - let output = context - .new_command() - .args("test test/recursive_permissions_pledge.js") - .run(); - output.assert_exit_code(1); - assert_contains!( - output.combined_output(), - "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(); - - TestContext::default() - .new_command() - .args_vec(["test", file_url.as_str()]) - .run() - .assert_matches_file("test/file_protocol.out"); -} - -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!(report_error { - args: "test --quiet test/report_error.ts", - output: "test/report_error.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, -}); - -#[test] -// todo(#18480): re-enable -#[ignore] -fn sigint_with_hanging_test() { - util::with_pty( - &[ - "test", - "--quiet", - "--no-check", - "test/sigint_with_hanging_test.ts", - ], - |mut console| { - std::thread::sleep(std::time::Duration::from_secs(1)); - console.write_line("\x03"); - let text = console.read_until("hanging_test.ts:10:15"); - wildcard_match( - include_str!("../testdata/test/sigint_with_hanging_test.out"), - &text, - ); - }, - ); -} - -itest!(package_json_basic { - args: "test", - output: "package_json/basic/lib.test.out", - envs: env_vars_for_npm_tests(), - http_server: true, - cwd: Some("package_json/basic"), - copy_temp_dir: Some("package_json/basic"), - exit_code: 0, -}); - -itest!(test_lock { - args: "test", - http_server: true, - cwd: Some("lockfile/basic"), - exit_code: 10, - output: "lockfile/basic/fail.out", -}); - -itest!(test_no_lock { - args: "test --no-lock", - http_server: true, - cwd: Some("lockfile/basic"), - output: "lockfile/basic/test.nolock.out", -}); - -itest!(test_replace_timers { - args: "test test/replace_timers.js", - output: "test/replace_timers.js.out", -}); - -#[test] -fn test_with_glob_config() { - let context = TestContextBuilder::new().cwd("test").build(); - - let cmd_output = context - .new_command() - .args("test --config deno.glob.json") - .run(); - - cmd_output.assert_exit_code(0); - - let output = cmd_output.combined_output(); - assert_contains!(output, "glob/nested/fizz/fizz.ts"); - assert_contains!(output, "glob/pages/[id].ts"); - assert_contains!(output, "glob/nested/fizz/bar.ts"); - assert_contains!(output, "glob/nested/foo/foo.ts"); - assert_contains!(output, "glob/data/test1.js"); - assert_contains!(output, "glob/nested/foo/bar.ts"); - assert_contains!(output, "glob/nested/foo/fizz.ts"); - assert_contains!(output, "glob/nested/fizz/foo.ts"); - assert_contains!(output, "glob/data/test1.ts"); -} - -#[test] -fn test_with_glob_config_and_flags() { - let context = TestContextBuilder::new().cwd("test").build(); - - let cmd_output = context - .new_command() - .args("test --config deno.glob.json --ignore=glob/nested/**/bar.ts") - .run(); - - cmd_output.assert_exit_code(0); - - let output = cmd_output.combined_output(); - assert_contains!(output, "glob/nested/fizz/fizz.ts"); - assert_contains!(output, "glob/pages/[id].ts"); - assert_contains!(output, "glob/nested/fizz/bazz.ts"); - assert_contains!(output, "glob/nested/foo/foo.ts"); - assert_contains!(output, "glob/data/test1.js"); - assert_contains!(output, "glob/nested/foo/bazz.ts"); - assert_contains!(output, "glob/nested/foo/fizz.ts"); - assert_contains!(output, "glob/nested/fizz/foo.ts"); - assert_contains!(output, "glob/data/test1.ts"); - - let cmd_output = context - .new_command() - .args("test --config deno.glob.json glob/data/test1.?s") - .run(); - - cmd_output.assert_exit_code(0); - - let output = cmd_output.combined_output(); - assert_contains!(output, "glob/data/test1.js"); - assert_contains!(output, "glob/data/test1.ts"); -} - -#[test] -fn conditionally_loads_type_graph() { - let context = TestContext::default(); - let output = context - .new_command() - .args("test --reload -L debug run/type_directives_js_main.js") - .run(); - output.assert_matches_text("[WILDCARD] - FileFetcher::fetch() - specifier: file:///[WILDCARD]/subdir/type_reference.d.ts[WILDCARD]"); - let output = context - .new_command() - .args("test --reload -L debug --no-check run/type_directives_js_main.js") - .run(); - assert_not_contains!(output.combined_output(), "type_reference.d.ts"); -} - -itest!(test_include_relative_pattern_dot_slash { - args: "test", - output: "test/relative_pattern_dot_slash/output.out", - cwd: Some("test/relative_pattern_dot_slash"), -}); diff --git a/cli/tests/integration/upgrade_tests.rs b/cli/tests/integration/upgrade_tests.rs deleted file mode 100644 index c016b61fc..000000000 --- a/cli/tests/integration/upgrade_tests.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::process::Command; -use std::process::Stdio; -use std::time::Instant; -use test_util as util; -use test_util::TempDir; -use util::TestContextBuilder; - -// 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"); - util::deno_exe_path().copy(&exe_path); - 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"); - util::deno_exe_path().copy(&exe_path); - 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"); - util::deno_exe_path().copy(&exe_path); - 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"); - util::deno_exe_path().copy(&exe_path); - 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"); - util::deno_exe_path().copy(&exe_path); - 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) - .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"); - util::deno_exe_path().copy(&exe_path); - 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"); - util::deno_exe_path().copy(&exe_path); - 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()) - ); -} - -#[test] -fn upgrade_prompt() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .env( - "DENO_DONT_USE_INTERNAL_BASE_UPGRADE_URL", - "http://localhost:4545", - ) - .build(); - let temp_dir = context.temp_dir(); - // start a task that goes indefinitely in order to allow - // the upgrade check to occur - temp_dir.write("main.js", "setInterval(() => {}, 1_000)"); - let cmd = context - .new_command() - .args("run --log-level=debug main.js") - .env_remove("DENO_NO_UPDATE_CHECK"); - // run once and wait for the version to be stored - cmd.with_pty(|mut pty| { - pty.expect("Finished upgrade checker."); - }); - // now check that the upgrade prompt is shown the next time this is run - temp_dir.write("main.js", ""); - cmd.with_pty(|mut pty| { - // - We need to use a pty here because the upgrade prompt - // doesn't occur except when there's a pty. - // - Version comes from the test server. - pty.expect_any(&[ - " 99999.99.99 Run `deno upgrade` to install it.", - // it builds canary releases on main, so check for this in that case - "Run `deno upgrade --canary` to install it.", - ]); - }); -} - -#[test] -fn upgrade_lsp_repl_sleeps() { - let context = TestContextBuilder::new() - .use_http_server() - .use_temp_cwd() - .env( - "DENO_DONT_USE_INTERNAL_BASE_UPGRADE_URL", - "http://localhost:4545/upgrade/sleep", - ) - .build(); - let start_instant = Instant::now(); - // ensure this works even though the upgrade check is taking - // a long time to complete - context - .new_command() - .args("repl") - .env_remove("DENO_NO_UPDATE_CHECK") - .with_pty(|mut pty| { - pty.write_line("123 + 456\n"); - pty.expect("579"); - }); - - // the test server will sleep for 95 seconds, so ensure this is less - let elapsed_secs = start_instant.elapsed().as_secs(); - assert!(elapsed_secs < 94, "elapsed_secs: {}", elapsed_secs); -} diff --git a/cli/tests/integration/vendor_tests.rs b/cli/tests/integration/vendor_tests.rs deleted file mode 100644 index c38fb653a..000000000 --- a/cli/tests/integration/vendor_tests.rs +++ /dev/null @@ -1,704 +0,0 @@ -// Copyright 2018-2024 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 test_util as util; -use test_util::TempDir; -use util::http_server; -use util::new_deno_dir; -use util::TestContextBuilder; - -#[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_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_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") - .piped_output() - .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") - .piped_output() - .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") - .piped_output() - .spawn() - .unwrap(); - let output = deno.wait_with_output().unwrap(); - assert_eq!( - String::from_utf8_lossy(&output.stderr).trim(), - format!( - concat!( - "{}\n", - "Download http://localhost:4545/vendor/logger.ts\n", - "{}\n\n{}", - ), - ignoring_import_map_text(), - vendored_text("1 module", "vendor/"), - success_text_updated_deno_json("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") - .piped_output() - .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_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") - .piped_output() - .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") - .piped_output() - .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") - .piped_output() - .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()); -} - -itest!(dynamic_non_existent { - args: "vendor http://localhost:4545/vendor/dynamic_non_existent.ts", - temp_cwd: true, - exit_code: 0, - http_server: true, - output: "vendor/dynamic_non_existent.ts.out", -}); - -#[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") - .piped_output() - .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{}\n\n{}", - vendored_text("1 module", "vendor2"), - success_text_updated_deno_json("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") - .piped_output() - .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 vendor_npm_node_specifiers() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "my_app.ts", - concat!( - "import { path, getValue, setValue } from 'http://localhost:4545/vendor/npm_and_node_specifier.ts';\n", - "setValue(5);\n", - "console.log(path.isAbsolute(Deno.cwd()), getValue());", - ), - ); - temp_dir.write("deno.json", "{}"); - - let output = context.new_command().args("vendor my_app.ts").run(); - output.assert_matches_text( - format!( - concat!( - "Download http://localhost:4545/vendor/npm_and_node_specifier.ts\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz\n", - "{}\n", - "Initialize @denotest/esm-basic@1.0.0\n", - "{}\n\n", - "{}\n", - ), - vendored_text("1 module", "vendor/"), - vendored_npm_package_text("1 npm package"), - success_text_updated_deno_json("vendor/") - ) - ); - let output = context.new_command().args("run -A my_app.ts").run(); - output.assert_matches_text("true 5\n"); - assert!(temp_dir.path().join("node_modules").exists()); - assert!(temp_dir.path().join("deno.lock").exists()); - - // now try re-vendoring with a lockfile - let output = context.new_command().args("vendor --force my_app.ts").run(); - output.assert_matches_text(format!( - "{}\n{}\n\n{}\n", - ignoring_import_map_text(), - vendored_text("1 module", "vendor/"), - success_text_updated_deno_json("vendor/"), - )); - - // delete the node_modules folder - temp_dir.remove_dir_all("node_modules"); - - // vendor with --node-modules-dir=false - let output = context - .new_command() - .args("vendor --node-modules-dir=false --force my_app.ts") - .run(); - output.assert_matches_text(format!( - "{}\n{}\n\n{}\n", - ignoring_import_map_text(), - vendored_text("1 module", "vendor/"), - success_text_updated_deno_json("vendor/") - )); - assert!(!temp_dir.path().join("node_modules").exists()); - - // delete the deno.json - temp_dir.remove_file("deno.json"); - - // vendor with --node-modules-dir - let output = context - .new_command() - .args("vendor --node-modules-dir --force my_app.ts") - .run(); - output.assert_matches_text(format!( - "Initialize @denotest/esm-basic@1.0.0\n{}\n\n{}\n", - vendored_text("1 module", "vendor/"), - use_import_map_text("vendor/") - )); -} - -#[test] -fn vendor_only_npm_specifiers() { - let context = TestContextBuilder::for_npm().use_temp_cwd().build(); - let temp_dir = context.temp_dir(); - temp_dir.write( - "my_app.ts", - concat!( - "import { getValue, setValue } from 'npm:@denotest/esm-basic';\n", - "setValue(5);\n", - "console.log(path.isAbsolute(Deno.cwd()), getValue());", - ), - ); - temp_dir.write("deno.json", "{}"); - - let output = context.new_command().args("vendor my_app.ts").run(); - output.assert_matches_text( - format!( - concat!( - "Download http://localhost:4545/npm/registry/@denotest/esm-basic\n", - "Download http://localhost:4545/npm/registry/@denotest/esm-basic/1.0.0.tgz\n", - "{}\n", - "Initialize @denotest/esm-basic@1.0.0\n", - "{}\n", - ), - vendored_text("0 modules", "vendor/"), - vendored_npm_package_text("1 npm package"), - ) - ); -} - -fn success_text(module_count: &str, dir: &str, has_import_map: bool) -> String { - let mut text = format!("Vendored {module_count} into {dir} directory."); - if has_import_map { - write!(text, "\n\n{}", use_import_map_text(dir)).unwrap(); - } - text -} - -fn use_import_map_text(dir: &str) -> String { - format!( - concat!( - "To 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() - } - ) -} - -fn vendored_text(module_count: &str, dir: &str) -> String { - format!("Vendored {} into {} directory.", module_count, dir) -} - -fn vendored_npm_package_text(package_count: &str) -> String { - format!( - concat!( - "Vendored {} into node_modules directory. Set `nodeModulesDir: false` ", - "in the Deno configuration file to disable vendoring npm packages in the future.", - ), - package_count - ) -} - -fn success_text_updated_deno_json(dir: &str) -> String { - format!( - concat!( - "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.", - ), - if dir != "vendor/" { - format!( - "{}{}", - dir.trim_end_matches('/'), - if cfg!(windows) { '\\' } else { '/' } - ) - } else { - dir.to_string() - } - ) -} - -fn ignoring_import_map_text() -> String { - 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.", - ), - PathBuf::from("vendor").join("import_map.json").display(), - ) -} diff --git a/cli/tests/integration/watcher_tests.rs b/cli/tests/integration/watcher_tests.rs deleted file mode 100644 index 6a2cab08a..000000000 --- a/cli/tests/integration/watcher_tests.rs +++ /dev/null @@ -1,1864 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use flaky_test::flaky_test; -use test_util as util; -use test_util::assert_contains; -use test_util::TempDir; -use tokio::io::AsyncBufReadExt; -use util::DenoChild; - -use util::assert_not_contains; - -const CLEAR_SCREEN: &str = r#"[2J"#; - -/// Logs to stderr every time next_line() is called -struct LoggingLines<R> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - pub lines: tokio::io::Lines<R>, - pub stream_name: String, -} - -impl<R> LoggingLines<R> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - pub async fn next_line(&mut self) -> tokio::io::Result<Option<String>> { - let line = self.lines.next_line().await; - eprintln!( - "{}: {}", - self.stream_name, - line.as_ref().unwrap().clone().unwrap() - ); - line - } -} - -// Helper function to skip watcher output that contains "Restarting" -// phrase. -async fn skip_restarting_line<R>(stderr_lines: &mut LoggingLines<R>) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - loop { - let msg = next_line(stderr_lines).await.unwrap(); - if !msg.contains("Restarting") { - return msg; - } - } -} - -async fn read_all_lints<R>(stderr_lines: &mut LoggingLines<R>) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let mut str = String::new(); - while let Some(t) = next_line(stderr_lines).await { - 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("error[") { - str.push_str(&t); - str.push('\n'); - } - } - str -} - -async fn next_line<R>(lines: &mut LoggingLines<R>) -> Option<String> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let timeout = tokio::time::Duration::from_secs(60); - - tokio::time::timeout(timeout, lines.next_line()) - .await - .unwrap_or_else(|_| { - panic!( - "Output did not contain a new line after {} seconds", - timeout.as_secs() - ) - }) - .unwrap() -} - -/// Returns the matched line or None if there are no more lines in this stream -async fn wait_for<R>( - condition: impl Fn(&str) -> bool, - lines: &mut LoggingLines<R>, -) -> Option<String> -where - R: tokio::io::AsyncBufRead + Unpin, -{ - while let Some(line) = lines.next_line().await.unwrap() { - if condition(line.as_str()) { - return Some(line); - } - } - - None -} - -async fn wait_contains<R>(s: &str, lines: &mut LoggingLines<R>) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let timeout = tokio::time::Duration::from_secs(60); - - tokio::time::timeout(timeout, wait_for(|line| line.contains(s), lines)) - .await - .unwrap_or_else(|_| { - panic!( - "Output did not contain \"{}\" after {} seconds", - s, - timeout.as_secs() - ) - }) - .unwrap_or_else(|| panic!("Output ended without containing \"{}\"", s)) -} - -/// Before test cases touch files, they need to wait for the watcher to be -/// ready. Waiting for subcommand output is insufficient. -/// The file watcher takes a moment to start watching files due to -/// asynchronicity. It is possible for the watched subcommand to finish before -/// any files are being watched. -/// deno must be running with --log-level=debug -/// file_name should be the file name and, optionally, extension. file_name -/// may not be a full path, as it is not portable. -async fn wait_for_watcher<R>( - file_name: &str, - stderr_lines: &mut LoggingLines<R>, -) -> String -where - R: tokio::io::AsyncBufRead + Unpin, -{ - let timeout = tokio::time::Duration::from_secs(60); - - tokio::time::timeout( - timeout, - wait_for( - |line| line.contains("Watching paths") && line.contains(file_name), - stderr_lines, - ), - ) - .await - .unwrap_or_else(|_| { - panic!( - "Watcher did not start for file \"{}\" after {} seconds", - file_name, - timeout.as_secs() - ) - }) - .unwrap_or_else(|| { - panic!( - "Output ended without before the watcher started watching file \"{}\"", - file_name - ) - }) -} - -fn check_alive_then_kill(mut child: DenoChild) { - assert!(child.try_wait().unwrap().is_none()); - child.kill().unwrap(); -} - -fn child_lines( - child: &mut std::process::Child, -) -> ( - LoggingLines<tokio::io::BufReader<tokio::process::ChildStdout>>, - LoggingLines<tokio::io::BufReader<tokio::process::ChildStderr>>, -) { - let stdout_lines = LoggingLines { - lines: tokio::io::BufReader::new( - tokio::process::ChildStdout::from_std(child.stdout.take().unwrap()) - .unwrap(), - ) - .lines(), - stream_name: "STDOUT".to_string(), - }; - let stderr_lines = LoggingLines { - lines: tokio::io::BufReader::new( - tokio::process::ChildStderr::from_std(child.stderr.take().unwrap()) - .unwrap(), - ) - .lines(), - stream_name: "STDERR".to_string(), - }; - (stdout_lines, stderr_lines) -} - -#[tokio::test] -async 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") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - let next_line = next_line(&mut stderr_lines).await.unwrap(); - - assert_contains!(&next_line, "Lint started"); - let mut output = read_all_lints(&mut stderr_lines).await; - 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).await; - 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).await; - 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); -} - -#[tokio::test] -async 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") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - let next_line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&next_line, "Lint started"); - let mut output = read_all_lints(&mut stderr_lines).await; - 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).await; - 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).await; - 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); -} - -#[tokio::test] -async 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") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 2 files" - ); - - std::fs::copy(badly_linted_fixed2, badly_linted_2).unwrap(); - - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 2 files" - ); - - assert!(child.try_wait().unwrap().is_none()); - - child.kill().unwrap(); - drop(t); -} - -#[tokio::test] -async 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") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - let next_line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&next_line, "Fmt started"); - assert_contains!( - skip_restarting_line(&mut stderr_lines).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 1 file" - ); - wait_contains("Fmt finished", &mut stderr_lines).await; - - 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).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "Checked 1 file" - ); - wait_contains("Fmt finished", &mut stderr_lines).await; - - // 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); -} - -#[tokio::test] -async 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") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - let next_line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&next_line, "Fmt started"); - assert_contains!( - skip_restarting_line(&mut stderr_lines).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "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).await, - "badly_formatted.js" - ); - assert_contains!( - wait_contains("Checked", &mut stderr_lines).await, - "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); -} - -#[ignore = "https://github.com/denoland/deno/issues/19629"] -#[tokio::test] -async 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") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_contains!( - wait_contains("error", &mut stderr_lines).await, - "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!( - wait_contains("error", &mut stderr_lines).await, - "Found 2 not formatted files in 2 files" - ); - - check_alive_then_kill(child); -} - -#[tokio::test] -async 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"); - file_to_watch.write("console.log('Hello world');"); - 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") - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - - let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Warning"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "deno_emit"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bundle started" - ); - let line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(line, "file_to_watch.ts"); - assert_contains!(line, "Check"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Bundle"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "mod6.bundle.js" - ); - let file = PathBuf::from(&bundle); - assert!(file.is_file()); - - wait_contains("Bundle finished", &mut stderr_lines).await; - - file_to_watch.write("console.log('Hello world2');"); - - let line = next_line(&mut stderr_lines).await.unwrap(); - // Should not clear screen, as we are in non-TTY environment - assert_not_contains!(&line, CLEAR_SCREEN); - assert_contains!(&line, "File change detected!"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Check"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "file_to_watch.ts" - ); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "mod6.bundle.js" - ); - let file = PathBuf::from(&bundle); - assert!(file.is_file()); - wait_contains("Bundle finished", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - file_to_watch.write("syntax error ^^"); - - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "File change detected!" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error: "); - wait_contains("Bundle failed", &mut stderr_lines).await; - check_alive_then_kill(deno); -} - -/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt -#[tokio::test] -async fn bundle_watch_not_exit() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.ts"); - file_to_watch.write("syntax error ^^"); - 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") - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_stdout_lines, mut stderr_lines) = child_lines(&mut deno); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Warning"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "deno_emit"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bundle started" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error:"); - assert_eq!(next_line(&mut stderr_lines).await.unwrap(), ""); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " syntax error ^^" - ); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " ~~~~~" - ); - assert_contains!( - next_line(&mut stderr_lines).await.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 - file_to_watch.write("console.log(42);"); - - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "File change detected" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Check"); - let line = next_line(&mut stderr_lines).await.unwrap(); - // Should not clear screen, as we are in non-TTY environment - assert_not_contains!(&line, CLEAR_SCREEN); - assert_contains!(line, "file_to_watch.ts"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "target.js"); - - wait_contains("Bundle finished", &mut stderr_lines).await; - - // bundled file is created - assert!(target_file.is_file()); - check_alive_then_kill(deno); -} - -#[tokio::test] -async fn run_watch_no_dynamic() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("console.log('Hello world');"); - - 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") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Hello world", &mut stdout_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Change content of the file - file_to_watch.write("console.log('Hello world2');"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Hello world2", &mut stdout_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Add dependency - let another_file = t.path().join("another_file.js"); - another_file.write("export const foo = 0;"); - file_to_watch - .write("import { foo } from './another_file.js'; console.log(foo);"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("0", &mut stdout_lines).await; - wait_for_watcher("another_file.js", &mut stderr_lines).await; - - // Confirm that restarting occurs when a new file is updated - another_file.write("export const foo = 42;"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("42", &mut stdout_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - file_to_watch.write("syntax error ^^"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("error:", &mut stderr_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Then restore the file - file_to_watch - .write("import { foo } from './another_file.js'; console.log(foo);"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("42", &mut stdout_lines).await; - wait_for_watcher("another_file.js", &mut stderr_lines).await; - - // Update the content of the imported file with invalid syntax - another_file.write("syntax error ^^"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("error:", &mut stderr_lines).await; - wait_for_watcher("another_file.js", &mut stderr_lines).await; - - // Modify the imported file and make sure that restarting occurs - another_file.write("export const foo = 'modified!';"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("modified!", &mut stdout_lines).await; - wait_contains("Watching paths", &mut stderr_lines).await; - 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"))] -#[tokio::test] -async fn run_watch_external_watch_files() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("console.log('Hello world');"); - - let external_file_to_watch = t.path().join("external_file_to_watch.txt"); - external_file_to_watch.write("Hello world"); - - let mut watch_arg = "--watch=".to_owned(); - let external_file_to_watch_str = external_file_to_watch.to_string(); - 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") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("Hello world", &mut stdout_lines).await; - wait_for_watcher("external_file_to_watch.txt", &mut stderr_lines).await; - - // Change content of the external file - external_file_to_watch.write("Hello world2"); - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - // Again (https://github.com/denoland/deno/issues/17584) - external_file_to_watch.write("Hello world3"); - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_load_unload_events() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - setInterval(() => {}, 0); - window.addEventListener("load", () => { - console.log("load"); - }); - - window.addEventListener("unload", () => { - console.log("unload"); - }); - "#, - ); - - 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") - .piped_output() - .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).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Change content of the file, this time without an interval to keep it alive. - file_to_watch.write( - r#" - window.addEventListener("load", () => { - console.log("load"); - }); - - window.addEventListener("unload", () => { - console.log("unload"); - }); - "#, - ); - - // Wait for the restart - wait_contains("Restarting", &mut stderr_lines).await; - - // Confirm that the unload event was dispatched from the first run - wait_contains("unload", &mut stdout_lines).await; - - // Followed by the load event of the second run - wait_contains("load", &mut stdout_lines).await; - - // Which is then unloaded as there is nothing keeping it alive. - wait_contains("unload", &mut stdout_lines).await; - check_alive_then_kill(child); -} - -/// Confirm that the watcher continues to work even if module resolution fails at the *first* attempt -#[tokio::test] -async fn run_watch_not_exit() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("syntax error ^^"); - - 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") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("error:", &mut stderr_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - - // Make sure the watcher actually restarts and works fine with the proper syntax - file_to_watch.write("console.log(42);"); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("42", &mut stdout_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -#[tokio::test] -async 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); - absolute_path.write(filecontent); - let relative_path = absolute_path - .as_path() - .strip_prefix(directory.path()) - .unwrap() - .to_owned(); - assert!(relative_path.is_relative()); - relative_path - } - - let temp_directory = TempDir::new(); - 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(temp_directory.path()) - .arg("run") - .arg("--watch") - .arg("--import-map") - .arg(&import_map_path) - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - let line = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line, "Process started"); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Process finished" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "Hello world"); - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_with_ext_flag() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch"); - file_to_watch.write("interface I{}; console.log(42);"); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--log-level") - .arg("debug") - .arg("--ext") - .arg("ts") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("42", &mut stdout_lines).await; - - // Make sure the watcher actually restarts and works fine with the proper language - wait_for_watcher("file_to_watch", &mut stderr_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - file_to_watch.write("type Bear = 'polar' | 'grizzly'; console.log(123);"); - - wait_contains("Restarting!", &mut stderr_lines).await; - wait_contains("123", &mut stdout_lines).await; - wait_contains("Process finished", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_error_messages() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch - .write("throw SyntaxError(`outer`, {cause: TypeError(`inner`)})"); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Process started", &mut stderr_lines).await; - wait_contains( - "error: Uncaught (in promise) SyntaxError: outer", - &mut stderr_lines, - ) - .await; - wait_contains("Caused by: TypeError: inner", &mut stderr_lines).await; - wait_contains("Process failed", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn test_watch_basic() { - 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") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_eq!(next_line(&mut stdout_lines).await.unwrap(), ""); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "0 passed | 0 failed" - ); - wait_contains("Test finished", &mut stderr_lines).await; - - 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"); - foo_file.write("export default function foo() { 1 + 1 }"); - bar_file.write("export default function bar() { 2 + 2 }"); - foo_test.write("import foo from './foo.js'; Deno.test('foo', foo);"); - bar_test.write("import bar from './bar.js'; Deno.test('bar', bar);"); - - assert_eq!(next_line(&mut stdout_lines).await.unwrap(), ""); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foo", "bar"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foo", "bar"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Change content of the file - foo_test.write("import foo from './foo.js'; Deno.test('foobar', foo);"); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foobar"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Add test - let another_test = t.path().join("new_test.js"); - another_test.write("Deno.test('another one', () => 3 + 3)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "another one"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Confirm that restarting occurs when a new file is updated - another_test.write("Deno.test('another one', () => 3 + 3); Deno.test('another another one', () => 4 + 4)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 2 tests" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "another one"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "another another one" - ); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - another_test.write("syntax error ^^"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error:"); - assert_eq!(next_line(&mut stderr_lines).await.unwrap(), ""); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " syntax error ^^" - ); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " ~~~~~" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Test failed"); - - // Then restore the file - another_test.write("Deno.test('another one', () => 3 + 3)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "another one"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // 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 - foo_file - .write("export default function foo() { throw new Error('Whoops!'); }"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "FAILED"); - wait_contains("FAILED", &mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test failed", &mut stderr_lines).await; - - // Then restore the file - foo_file.write("export default function foo() { 1 + 1 }"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "running 1 test" - ); - assert_contains!(next_line(&mut stdout_lines).await.unwrap(), "foo"); - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - next_line(&mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - - // Test that circular dependencies work fine - foo_file.write("import './bar.js'; export default function foo() { 1 + 1 }"); - bar_file.write("import './foo.js'; export default function bar() { 2 + 2 }"); - check_alive_then_kill(child); -} - -#[flaky_test] -#[tokio::main] -async 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") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_eq!(next_line(&mut stdout_lines).await.unwrap(), ""); - assert_contains!( - next_line(&mut stdout_lines).await.unwrap(), - "0 passed | 0 failed" - ); - wait_contains("Test finished", &mut stderr_lines).await; - - let foo_file = t.path().join("foo.ts"); - foo_file.write( - r#" - export default function foo() {} - "#, - ); - - foo_file.write( - r#" - /** - * ```ts - * import foo from "./foo.ts"; - * ``` - */ - export default function foo() {} - "#, - ); - - // 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).await, "foo.ts$3-6"); - check_alive_then_kill(child); -} - -#[tokio::test] -async fn test_watch_module_graph_error_referrer() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write("import './nonexistent.js';"); - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_, mut stderr_lines) = child_lines(&mut child); - let line1 = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line1, "Process started"); - let line2 = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line2, "error: Module not found"); - assert_contains!(&line2, "nonexistent.js"); - let line3 = next_line(&mut stderr_lines).await.unwrap(); - assert_contains!(&line3, " at "); - assert_contains!(&line3, "file_to_watch.js"); - wait_contains("Process failed", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -// Regression test for https://github.com/denoland/deno/issues/15428. -#[tokio::test] -async fn test_watch_unload_handler_error_on_drop() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - addEventListener("unload", () => { - throw new Error("foo"); - }); - setTimeout(() => { - throw new Error("bar"); - }); - "#, - ); - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (_, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("Uncaught Error: bar", &mut stderr_lines).await; - wait_contains("Process failed", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_blob_urls_reset() { - let _g = util::http_server(); - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - let file_content = r#" - const prevUrl = localStorage.getItem("url"); - if (prevUrl == null) { - console.log("first run, storing blob url"); - const url = URL.createObjectURL( - new Blob(["export {}"], { type: "application/javascript" }), - ); - await import(url); // this shouldn't insert into the fs module cache - localStorage.setItem("url", url); - } else { - await import(prevUrl) - .then(() => console.log("importing old blob url incorrectly works")) - .catch(() => console.log("importing old blob url correctly failed")); - } - "#; - file_to_watch.write(file_content); - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("first run, storing blob url", &mut stdout_lines).await; - wait_contains("finished", &mut stderr_lines).await; - file_to_watch.write(file_content); - wait_contains("importing old blob url correctly failed", &mut stdout_lines) - .await; - wait_contains("finished", &mut stderr_lines).await; - check_alive_then_kill(child); -} - -#[cfg(unix)] -#[tokio::test] -async fn test_watch_sigint() { - use nix::sys::signal; - use nix::sys::signal::Signal; - use nix::unistd::Pid; - use util::TestContext; - - let context = TestContext::default(); - let t = context.temp_dir(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write(r#"Deno.test("foo", () => {});"#); - let mut child = context - .new_command() - .args_vec(["test", "--watch", &file_to_watch.to_string_lossy()]) - .env("NO_COLOR", "1") - .spawn_with_piped_output(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Test started", &mut stderr_lines).await; - wait_contains("ok | 1 passed | 0 failed", &mut stdout_lines).await; - wait_contains("Test finished", &mut stderr_lines).await; - signal::kill(Pid::from_raw(child.id() as i32), Signal::SIGINT).unwrap(); - let exit_status = child.wait().unwrap(); - assert_eq!(exit_status.code(), Some(130)); -} - -#[tokio::test] -async fn bench_watch_basic() { - let t = TempDir::new(); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("bench") - .arg("--watch") - .arg("--no-check") - .arg(t.path()) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bench started" - ); - assert_contains!( - next_line(&mut stderr_lines).await.unwrap(), - "Bench finished" - ); - - let foo_file = t.path().join("foo.js"); - let bar_file = t.path().join("bar.js"); - let foo_bench = t.path().join("foo_bench.js"); - let bar_bench = t.path().join("bar_bench.js"); - foo_file.write("export default function foo() { 1 + 1 }"); - bar_file.write("export default function bar() { 2 + 2 }"); - foo_bench.write("import foo from './foo.js'; Deno.bench('foo bench', foo);"); - bar_bench.write("import bar from './bar.js'; Deno.bench('bar bench', bar);"); - - wait_contains("bar_bench.js", &mut stdout_lines).await; - wait_contains("bar bench", &mut stdout_lines).await; - wait_contains("foo_bench.js", &mut stdout_lines).await; - wait_contains("foo bench", &mut stdout_lines).await; - wait_contains("Bench finished", &mut stderr_lines).await; - - // Change content of the file - foo_bench.write("import foo from './foo.js'; Deno.bench('foo asdf', foo);"); - - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - if line.contains("foo asdf") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Add bench - let another_test = t.path().join("new_bench.js"); - another_test.write("Deno.bench('another one', () => 3 + 3)"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - assert_not_contains!(line, "foo"); - if line.contains("another one") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Confirm that restarting occurs when a new file is updated - another_test.write("Deno.bench('another one', () => 3 + 3); Deno.bench('another another one', () => 4 + 4)"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - assert_not_contains!(line, "foo"); - if line.contains("another another one") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Confirm that the watcher keeps on working even if the file is updated and has invalid syntax - another_test.write("syntax error ^^"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "error:"); - assert_eq!(next_line(&mut stderr_lines).await.unwrap(), ""); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " syntax error ^^" - ); - assert_eq!( - next_line(&mut stderr_lines).await.unwrap(), - " ~~~~~" - ); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Bench failed"); - - // Then restore the file - another_test.write("Deno.bench('another one', () => 3 + 3)"); - assert_contains!(next_line(&mut stderr_lines).await.unwrap(), "Restarting"); - loop { - let line = next_line(&mut stdout_lines).await.unwrap(); - assert_not_contains!(line, "bar"); - assert_not_contains!(line, "foo"); - if line.contains("another one") { - break; // last line - } - } - wait_contains("Bench finished", &mut stderr_lines).await; - - // Test that circular dependencies work fine - foo_file.write("import './bar.js'; export default function foo() { 1 + 1 }"); - bar_file.write("import './foo.js'; export default function bar() { 2 + 2 }"); - check_alive_then_kill(child); -} - -// Regression test for https://github.com/denoland/deno/issues/15465. -#[tokio::test] -async 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); - "#; - file_to_watch.write(file_content); - - 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") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("finished", &mut stderr_lines).await; - let first_output = next_line(&mut stdout_lines).await.unwrap(); - - file_to_watch.write(file_content); - // The remote dynamic module should not have been reloaded again. - - wait_contains("finished", &mut stderr_lines).await; - let second_output = next_line(&mut stdout_lines).await.unwrap(); - assert_eq!(second_output, first_output); - - check_alive_then_kill(child); -} - -/// Regression test for https://github.com/denoland/deno/issues/18960. Ensures that Deno.serve -/// operates properly after a watch restart. -#[tokio::test] -async fn test_watch_serve() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - let file_content = r#" - console.error("serving"); - await Deno.serve({port: 4600, handler: () => new Response("hello")}); - "#; - file_to_watch.write(file_content); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--allow-net") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Listening on", &mut stdout_lines).await; - // Note that we start serving very quickly, so we specifically want to wait for this message - wait_contains(r#"Watching paths: [""#, &mut stderr_lines).await; - - file_to_watch.write(file_content); - - wait_contains("serving", &mut stderr_lines).await; - wait_contains("Listening on", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_dynamic_imports() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - console.log("Hopefully dynamic import will be watched..."); - await import("./imported.js"); - "#, - ); - let file_to_watch2 = t.path().join("imported.js"); - file_to_watch2.write( - r#" - import "./imported2.js"; - console.log("I'm dynamically imported and I cause restarts!"); - "#, - ); - let file_to_watch3 = t.path().join("imported2.js"); - file_to_watch3.write( - r#" - console.log("I'm statically imported from the dynamic import"); - "#, - ); - - 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") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_contains( - "Hopefully dynamic import will be watched...", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm statically imported from the dynamic import", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm dynamically imported and I cause restarts!", - &mut stdout_lines, - ) - .await; - - wait_for_watcher("imported2.js", &mut stderr_lines).await; - wait_contains("finished", &mut stderr_lines).await; - - file_to_watch3.write( - r#" - console.log("I'm statically imported from the dynamic import and I've changed"); - "#, - ); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains( - "Hopefully dynamic import will be watched...", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm statically imported from the dynamic import and I've changed", - &mut stdout_lines, - ) - .await; - wait_contains( - "I'm dynamically imported and I cause restarts!", - &mut stdout_lines, - ) - .await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_watch_inspect() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" - console.log("hello world"); - "#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--watch") - .arg("--inspect") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - - wait_contains("Debugger listening", &mut stderr_lines).await; - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("hello world", &mut stdout_lines).await; - - file_to_watch.write( - r#" - console.log("updated file"); - "#, - ); - - wait_contains("Restarting", &mut stderr_lines).await; - wait_contains("Debugger listening", &mut stderr_lines).await; - wait_contains("updated file", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_server() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -globalThis.state = { i: 0 }; - -function bar() { - globalThis.state.i = 0; - console.log("got request", globalThis.state.i); -} - -function handler(_req) { - bar(); - return new Response("Hello world!"); -} - -Deno.serve({ port: 11111 }, handler); -console.log("Listening...") - "#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("--allow-net") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("Listening...", &mut stdout_lines).await; - - file_to_watch.write( - r#" -globalThis.state = { i: 0 }; - -function bar() { - globalThis.state.i = 0; - console.log("got request1", globalThis.state.i); -} - -function handler(_req) { - bar(); - return new Response("Hello world!"); -} - -Deno.serve({ port: 11111 }, handler); -console.log("Listening...") - "#, - ); - - wait_contains("Failed to reload module", &mut stderr_lines).await; - wait_contains("File change detected", &mut stderr_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_jsx() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -let i = 0; -setInterval(() => { - console.log(i++, foo()); -}, 100); -"#, - ); - let file_to_watch2 = t.path().join("foo.jsx"); - file_to_watch2.write( - r#" -export function foo() { - return `<h1>Hello</h1>`; -} -"#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("5 <h1>Hello</h1>", &mut stdout_lines).await; - - file_to_watch2.write( - r#" -export function foo() { - return `<h1>Hello world</h1>`; -} - "#, - ); - - wait_contains("Replaced changed module", &mut stderr_lines).await; - wait_contains("<h1>Hello world</h1>", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_uncaught_error() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -let i = 0; -setInterval(() => { - console.log(i++, foo()); -}, 100); -"#, - ); - let file_to_watch2 = t.path().join("foo.jsx"); - file_to_watch2.write( - r#" -export function foo() { - setTimeout(() => { - throw new Error("fail"); - }); - return `<h1>asd1</h1>`; -} -"#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("<h1>asd1</h1>", &mut stdout_lines).await; - wait_contains("fail", &mut stderr_lines).await; - - file_to_watch2.write( - r#" -export function foo() { - return `<h1>asd2</h1>`; -} - "#, - ); - - wait_contains("Process failed", &mut stderr_lines).await; - wait_contains("File change detected", &mut stderr_lines).await; - wait_contains("<h1>asd2</h1>", &mut stdout_lines).await; - - check_alive_then_kill(child); -} - -#[tokio::test] -async fn run_hmr_unhandled_rejection() { - let t = TempDir::new(); - let file_to_watch = t.path().join("file_to_watch.js"); - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -// deno-lint-ignore require-await -async function rejection() { - throw new Error("boom!"); -} - -let i = 0; -setInterval(() => { - if (i == 3) { - rejection(); - } - console.log(i++, foo()); -}, 100); -"#, - ); - let file_to_watch2 = t.path().join("foo.jsx"); - file_to_watch2.write( - r#" -export function foo() { - return `<h1>asd1</h1>`; -} -"#, - ); - - let mut child = util::deno_cmd() - .current_dir(util::testdata_path()) - .arg("run") - .arg("--unstable-hmr") - .arg("-L") - .arg("debug") - .arg(&file_to_watch) - .env("NO_COLOR", "1") - .piped_output() - .spawn() - .unwrap(); - let (mut stdout_lines, mut stderr_lines) = child_lines(&mut child); - wait_contains("Process started", &mut stderr_lines).await; - wait_contains("No package.json file found", &mut stderr_lines).await; - - wait_for_watcher("file_to_watch.js", &mut stderr_lines).await; - wait_contains("2 <h1>asd1</h1>", &mut stdout_lines).await; - wait_contains("boom", &mut stderr_lines).await; - - file_to_watch.write( - r#" -import { foo } from "./foo.jsx"; - -let i = 0; -setInterval(() => { - console.log(i++, foo()); -}, 100); - "#, - ); - - wait_contains("Process failed", &mut stderr_lines).await; - wait_contains("File change detected", &mut stderr_lines).await; - wait_contains("<h1>asd1</h1>", &mut stdout_lines).await; - - check_alive_then_kill(child); -} diff --git a/cli/tests/integration/worker_tests.rs b/cli/tests/integration/worker_tests.rs deleted file mode 100644 index e2d1ef868..000000000 --- a/cli/tests/integration/worker_tests.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -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-worker-options 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, -}); - -// Regression test for https://github.com/denoland/deno/issues/19903 -itest!(worker_doest_stall_event_loop { - args: "run --quiet -A workers/worker_doest_stall_event_loop.ts", - output: "workers/worker_doest_stall_event_loop.ts.out", - exit_code: 0, -}); |