summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml1
-rw-r--r--cli/Cargo.toml2
-rw-r--r--tests/Cargo.toml7
-rw-r--r--tests/integration/cache_tests.rs6
-rw-r--r--tests/integration/info_tests.rs11
-rw-r--r--tests/integration/lsp_tests.rs6
-rw-r--r--tests/integration/npm_tests.rs48
-rw-r--r--tests/integration/run_tests.rs34
-rw-r--r--tests/specs/README.md92
-rw-r--r--tests/specs/import_map/import_map_config/__test__.json28
-rw-r--r--tests/specs/import_map/import_map_config/cache.out (renamed from tests/testdata/cache/036_import_map_fetch.out)0
-rw-r--r--tests/specs/import_map/import_map_config/config.json (renamed from tests/testdata/import_maps/config.json)0
-rw-r--r--tests/specs/import_map/import_map_config/config.out (renamed from tests/testdata/run/033_import_map_in_config_file.out)0
-rw-r--r--tests/specs/import_map/import_map_config/flag_has_precedence.out (renamed from tests/testdata/run/033_import_map_in_flag_has_precedence.out)0
-rw-r--r--tests/specs/import_map/import_map_config/import_data_url.out3
-rw-r--r--tests/specs/import_map/import_map_config/import_data_url.ts12
-rw-r--r--tests/specs/import_map/import_map_config/import_map.json (renamed from tests/testdata/import_maps/import_map.json)1
-rw-r--r--tests/specs/import_map/import_map_config/import_map_invalid.json (renamed from tests/testdata/import_maps/import_map_invalid.json)0
-rw-r--r--tests/specs/import_map/import_map_config/info.out (renamed from tests/testdata/info/065_import_map_info.out)0
-rw-r--r--tests/specs/import_map/import_map_config/lodash/lodash.ts1
-rw-r--r--tests/specs/import_map/import_map_config/lodash/other_file.ts1
-rw-r--r--tests/specs/import_map/import_map_config/moment/moment.ts1
-rw-r--r--tests/specs/import_map/import_map_config/moment/other_file.ts1
-rw-r--r--tests/specs/import_map/import_map_config/run.out (renamed from tests/testdata/run/033_import_map.out)0
-rw-r--r--tests/specs/import_map/import_map_config/scope/scoped.ts (renamed from tests/testdata/import_maps/scope/scoped.ts)0
-rw-r--r--tests/specs/import_map/import_map_config/scoped_moment.ts (renamed from tests/testdata/import_maps/scoped_moment.ts)0
-rw-r--r--tests/specs/import_map/import_map_config/test.ts6
-rw-r--r--tests/specs/import_map/import_map_config/unmapped_bare_specifier.out6
-rw-r--r--tests/specs/import_map/import_map_config/unmapped_bare_specifier.ts (renamed from tests/testdata/run/092_import_map_unmapped_bare_specifier.ts)0
-rw-r--r--tests/specs/import_map/import_map_config/vue.ts1
-rw-r--r--tests/specs/info/ts_error/__test__.json4
-rw-r--r--tests/specs/info/ts_error/info_ts_error.out6
-rw-r--r--tests/specs/info/ts_error/info_ts_error.ts (renamed from tests/testdata/info/031_info_ts_error.ts)0
-rw-r--r--tests/specs/mod.rs279
-rw-r--r--tests/specs/npm/conditional_exports/__test__.json5
-rw-r--r--tests/specs/npm/conditional_exports/main.js (renamed from tests/testdata/npm/conditional_exports/main.js)0
-rw-r--r--tests/specs/npm/conditional_exports/main.out (renamed from tests/testdata/npm/conditional_exports/main.out)0
-rw-r--r--tests/specs/npm/conditional_exports_node_modules_dir/__test__.json6
-rw-r--r--tests/specs/npm/conditional_exports_node_modules_dir/main.js15
-rw-r--r--tests/specs/npm/conditional_exports_node_modules_dir/main.out (renamed from tests/testdata/npm/conditional_exports/main_node_modules.out)0
-rw-r--r--tests/specs/npm/es_module/__test__.json22
-rw-r--r--tests/specs/npm/es_module/bundle.out1
-rw-r--r--tests/specs/npm/es_module/main.js (renamed from tests/testdata/npm/esm/main.js)0
-rw-r--r--tests/specs/npm/es_module/main.out (renamed from tests/testdata/npm/esm/main.out)0
-rw-r--r--tests/specs/npm/es_module/test.js (renamed from tests/testdata/npm/esm/test.js)0
-rw-r--r--tests/specs/npm/es_module/test.out (renamed from tests/testdata/npm/esm/test.out)2
-rw-r--r--tests/specs/run/redirect_javascript/__test__.json4
-rw-r--r--tests/specs/run/redirect_javascript/main.js (renamed from tests/testdata/run/026_redirect_javascript.js)0
-rw-r--r--tests/specs/run/redirect_javascript/main.out (renamed from tests/testdata/run/026_redirect_javascript.js.out)0
-rw-r--r--tests/testdata/import_maps/import_map_remote.json3
-rw-r--r--tests/testdata/info/031_info_ts_error.out6
-rw-r--r--tests/testdata/run/092_import_map_unmapped_bare_specifier.ts.out6
-rw-r--r--tests/util/server/Cargo.toml1
-rw-r--r--tests/util/server/src/fs.rs34
-rw-r--r--tools/copyright_checker.js1
56 files changed, 544 insertions, 121 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4fa6abace..9eb1c5d2e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -644,6 +644,7 @@ dependencies = [
"deno_core",
"deno_fetch",
"deno_lockfile",
+ "deno_terminal",
"deno_tls",
"fastwebsockets",
"flaky_test",
@@ -6517,6 +6518,7 @@ dependencies = [
"http-body-util",
"hyper 1.1.0",
"hyper-util",
+ "jsonc-parser",
"lazy-regex",
"libc",
"lsp-types",
diff --git a/Cargo.toml b/Cargo.toml
index afd1e7800..261a6b84f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -121,6 +121,7 @@ hyper = { version = "=1.1.0", features = ["full"] }
hyper-util = { version = "=0.1.2", features = ["tokio", "server", "server-auto"] }
hyper_v014 = { package = "hyper", version = "0.14.26", features = ["runtime", "http1"] }
indexmap = { version = "2", features = ["serde"] }
+jsonc-parser = { version = "=0.23.0", features = ["serde"] }
lazy-regex = "3"
libc = "0.2.126"
libz-sys = { version = "1.1", default-features = false }
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 77e67d118..86511465c 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -109,7 +109,7 @@ glob = "0.3.1"
ignore = "0.4"
import_map = { version = "=0.19.0", features = ["ext"] }
indexmap.workspace = true
-jsonc-parser = { version = "=0.23.0", features = ["serde"] }
+jsonc-parser.workspace = true
lazy-regex.workspace = true
libc.workspace = true
libz-sys.workspace = true
diff --git a/tests/Cargo.toml b/tests/Cargo.toml
index ffef6eb2d..872920bd9 100644
--- a/tests/Cargo.toml
+++ b/tests/Cargo.toml
@@ -21,6 +21,12 @@ path = "integration/mod.rs"
required-features = ["run"]
[[test]]
+name = "specs"
+path = "specs/mod.rs"
+required-features = ["run"]
+harness = false
+
+[[test]]
name = "node_compat_tests"
path = "node_compat/test_runner.rs"
required-features = ["run"]
@@ -33,6 +39,7 @@ deno_cache_dir = { workspace = true }
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting", "unsafe_use_unprotected_platform"] }
deno_fetch.workspace = true
deno_lockfile.workspace = true
+deno_terminal.workspace = true
deno_tls.workspace = true
fastwebsockets = { workspace = true, features = ["upgrade", "unstable-split"] }
flaky_test = "=0.1.0"
diff --git a/tests/integration/cache_tests.rs b/tests/integration/cache_tests.rs
index d5b4e8844..988cbc996 100644
--- a/tests/integration/cache_tests.rs
+++ b/tests/integration/cache_tests.rs
@@ -5,12 +5,6 @@ use test_util::itest;
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,
diff --git a/tests/integration/info_tests.rs b/tests/integration/info_tests.rs
index 62e169c9e..4697cc2b8 100644
--- a/tests/integration/info_tests.rs
+++ b/tests/integration/info_tests.rs
@@ -58,11 +58,6 @@ itest!(multiple_imports {
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",
@@ -95,12 +90,6 @@ itest!(json_file {
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",
diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs
index a7193ff59..f1a368413 100644
--- a/tests/integration/lsp_tests.rs
+++ b/tests/integration/lsp_tests.rs
@@ -215,7 +215,7 @@ fn lsp_import_map_remote() {
temp_dir.write(
"deno.json",
json!({
- "importMap": "http://localhost:4545/import_maps/import_map.json",
+ "importMap": "http://localhost:4545/import_maps/import_map_remote.json",
})
.to_string(),
);
@@ -228,7 +228,9 @@ fn lsp_import_map_remote() {
);
let mut client = context.new_lsp_command().build();
client.initialize(|builder| {
- builder.set_import_map("http://localhost:4545/import_maps/import_map.json");
+ builder.set_import_map(
+ "http://localhost:4545/import_maps/import_map_remote.json",
+ );
});
client.write_request(
"workspace/executeCommand",
diff --git a/tests/integration/npm_tests.rs b/tests/integration/npm_tests.rs
index 7df9e4f8a..d46c0544c 100644
--- a/tests/integration/npm_tests.rs
+++ b/tests/integration/npm_tests.rs
@@ -13,30 +13,6 @@ 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",
@@ -130,22 +106,6 @@ itest!(compare_globals {
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",
@@ -1011,14 +971,6 @@ fn ensure_registry_files_local() {
}
}
-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",
diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs
index 637dc578b..1d46cd249 100644
--- a/tests/integration/run_tests.rs
+++ b/tests/integration/run_tests.rs
@@ -154,12 +154,6 @@ itest!(_025_reload_js_type_error {
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",
@@ -180,23 +174,6 @@ itest!(_028_args {
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",
@@ -766,12 +743,6 @@ itest!(_091_use_define_for_class_fields {
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",
@@ -2233,11 +2204,6 @@ itest!(import_data_url_import_relative {
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",
diff --git a/tests/specs/README.md b/tests/specs/README.md
new file mode 100644
index 000000000..bbd9b880c
--- /dev/null
+++ b/tests/specs/README.md
@@ -0,0 +1,92 @@
+# specs
+
+These are integration tests that execute the `deno` binary. They supersede the
+`itest` macro found in the `tests/integration` folder and are the preferred way
+of writing tests that use the `deno` binary.
+
+## Structure
+
+Tests must have the following directory structure:
+
+```
+tests/specs/<category_name>/<test_name>/__test__.json
+```
+
+## Test filtering
+
+To run a specific test, run:
+
+```
+cargo test specs::category_name::test_name
+```
+
+Or just the following, though it might run other tests:
+
+```
+cargo test test_name
+```
+
+## `__test__.json` file
+
+This file describes the test to execute and the steps to execute. A basic
+example looks like:
+
+```json
+{
+ "args": "run main.js",
+ "output": "main.out"
+}
+```
+
+This will run `deno run main.js` then assert that the output matches the text in
+`main.out`.
+
+Or another example that runs multiple steps:
+
+```json
+{
+ "tempDir": true,
+ "steps": [{
+ "args": "cache main.ts",
+ "output": "cache.out"
+ }, {
+ "args": "run main.ts",
+ "output": "error.out",
+ "exitCode": 1
+ }]
+}
+```
+
+### Top level properties
+
+- `base` - The base config to use for the test. Options:
+ - `jsr` - Uses env vars for jsr.
+ - `npm` - Uses env vars for npm.
+- `tempDir` (boolean) - Copy all the non-test files to a temporary directory and
+ execute the command in that temporary directory.
+ - By default, tests are executed with a current working directory of the test,
+ but this may not be desirable for tests such as ones that create a
+ node_modules directory.
+
+### Step properties
+
+When writing a single step, these may be at the top level rather than nested in
+a "steps" array.
+
+- `args` - A string (that will be spilt on whitespace into an args array) or an
+ array of arguments.
+- `output` - Path to use to assert the output.
+- `clean` (boolean) - Whether to empty the deno_dir before running the step.
+- `exitCode` (number) - Expected exit code.
+
+## `.out` files
+
+`.out` files are used to assert the output when running a test or test step.
+
+Within the file, you can use the following for matching:
+
+- `[WILDCARD]` - match any text at the wildcard
+- `[WILDLINE]` - match any text on the current line
+- `[WILDCHARS(5)]` - match any of the next 5 characters
+- `[UNORDERED_START]` followed by many lines then `[UNORDERED_END]` will match
+ the lines in any order (useful for non-deterministic output)
diff --git a/tests/specs/import_map/import_map_config/__test__.json b/tests/specs/import_map/import_map_config/__test__.json
new file mode 100644
index 000000000..b0463a231
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/__test__.json
@@ -0,0 +1,28 @@
+{
+ "steps": [{
+ "args": "run --quiet --reload --import-map=import_map.json test.ts",
+ "output": "run.out"
+ }, {
+ "args": "run --quiet --reload --import-map=import_map_invalid.json --config=config.json test.ts",
+ "output": "flag_has_precedence.out",
+ "exitCode": 1
+ }, {
+ "args": "run --reload --config=config.json test.ts",
+ "output": "config.out"
+ }, {
+ "cleanDenoDir": true,
+ "args": "cache --quiet --reload --import-map=import_map.json test.ts",
+ "output": "cache.out"
+ }, {
+ "cleanDenoDir": true,
+ "args": "info --quiet --import-map=import_map.json test.ts",
+ "output": "info.out"
+ }, {
+ "args": "run --quiet --reload --import-map=import_map.json unmapped_bare_specifier.ts",
+ "output": "unmapped_bare_specifier.out",
+ "exitCode": 1
+ }, {
+ "args": "run --quiet --reload --import-map import_map.json import_data_url.ts",
+ "output": "import_data_url.out"
+ }]
+}
diff --git a/tests/testdata/cache/036_import_map_fetch.out b/tests/specs/import_map/import_map_config/cache.out
index e69de29bb..e69de29bb 100644
--- a/tests/testdata/cache/036_import_map_fetch.out
+++ b/tests/specs/import_map/import_map_config/cache.out
diff --git a/tests/testdata/import_maps/config.json b/tests/specs/import_map/import_map_config/config.json
index b296a63c7..b296a63c7 100644
--- a/tests/testdata/import_maps/config.json
+++ b/tests/specs/import_map/import_map_config/config.json
diff --git a/tests/testdata/run/033_import_map_in_config_file.out b/tests/specs/import_map/import_map_config/config.out
index 72df124a2..72df124a2 100644
--- a/tests/testdata/run/033_import_map_in_config_file.out
+++ b/tests/specs/import_map/import_map_config/config.out
diff --git a/tests/testdata/run/033_import_map_in_flag_has_precedence.out b/tests/specs/import_map/import_map_config/flag_has_precedence.out
index e9b183ee6..e9b183ee6 100644
--- a/tests/testdata/run/033_import_map_in_flag_has_precedence.out
+++ b/tests/specs/import_map/import_map_config/flag_has_precedence.out
diff --git a/tests/specs/import_map/import_map_config/import_data_url.out b/tests/specs/import_map/import_map_config/import_data_url.out
new file mode 100644
index 000000000..bfa0b9d94
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/import_data_url.out
@@ -0,0 +1,3 @@
+a
+{ "0": "A", "1": "B", "2": "C", A: 0, B: 1, C: 2 }
+0
diff --git a/tests/specs/import_map/import_map_config/import_data_url.ts b/tests/specs/import_map/import_map_config/import_data_url.ts
new file mode 100644
index 000000000..258514a5e
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/import_data_url.ts
@@ -0,0 +1,12 @@
+// export const a = "a";
+
+// export enum A {
+// A,
+// B,
+// C,
+// }
+import * as a from "data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=";
+
+console.log(a.a);
+console.log(a.A);
+console.log(a.A.A);
diff --git a/tests/testdata/import_maps/import_map.json b/tests/specs/import_map/import_map_config/import_map.json
index 40d1d4ec2..601874aab 100644
--- a/tests/testdata/import_maps/import_map.json
+++ b/tests/specs/import_map/import_map_config/import_map.json
@@ -1,6 +1,5 @@
{
"imports": {
- "print_hello": "./print_hello.ts",
"moment": "./moment/moment.ts",
"moment/": "./moment/",
"lodash": "./lodash/lodash.ts",
diff --git a/tests/testdata/import_maps/import_map_invalid.json b/tests/specs/import_map/import_map_config/import_map_invalid.json
index a09d280c5..a09d280c5 100644
--- a/tests/testdata/import_maps/import_map_invalid.json
+++ b/tests/specs/import_map/import_map_config/import_map_invalid.json
diff --git a/tests/testdata/info/065_import_map_info.out b/tests/specs/import_map/import_map_config/info.out
index 68d98f14a..68d98f14a 100644
--- a/tests/testdata/info/065_import_map_info.out
+++ b/tests/specs/import_map/import_map_config/info.out
diff --git a/tests/specs/import_map/import_map_config/lodash/lodash.ts b/tests/specs/import_map/import_map_config/lodash/lodash.ts
new file mode 100644
index 000000000..2ec04ed3c
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/lodash/lodash.ts
@@ -0,0 +1 @@
+console.log("Hello from remapped lodash!");
diff --git a/tests/specs/import_map/import_map_config/lodash/other_file.ts b/tests/specs/import_map/import_map_config/lodash/other_file.ts
new file mode 100644
index 000000000..714adae3f
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/lodash/other_file.ts
@@ -0,0 +1 @@
+console.log("Hello from remapped lodash dir!");
diff --git a/tests/specs/import_map/import_map_config/moment/moment.ts b/tests/specs/import_map/import_map_config/moment/moment.ts
new file mode 100644
index 000000000..2b54a431e
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/moment/moment.ts
@@ -0,0 +1 @@
+console.log("Hello from remapped moment!");
diff --git a/tests/specs/import_map/import_map_config/moment/other_file.ts b/tests/specs/import_map/import_map_config/moment/other_file.ts
new file mode 100644
index 000000000..24f3a0226
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/moment/other_file.ts
@@ -0,0 +1 @@
+console.log("Hello from remapped moment dir!");
diff --git a/tests/testdata/run/033_import_map.out b/tests/specs/import_map/import_map_config/run.out
index e9b9160e9..e9b9160e9 100644
--- a/tests/testdata/run/033_import_map.out
+++ b/tests/specs/import_map/import_map_config/run.out
diff --git a/tests/testdata/import_maps/scope/scoped.ts b/tests/specs/import_map/import_map_config/scope/scoped.ts
index 9a0b5d8e3..9a0b5d8e3 100644
--- a/tests/testdata/import_maps/scope/scoped.ts
+++ b/tests/specs/import_map/import_map_config/scope/scoped.ts
diff --git a/tests/testdata/import_maps/scoped_moment.ts b/tests/specs/import_map/import_map_config/scoped_moment.ts
index 9f67f88d4..9f67f88d4 100644
--- a/tests/testdata/import_maps/scoped_moment.ts
+++ b/tests/specs/import_map/import_map_config/scoped_moment.ts
diff --git a/tests/specs/import_map/import_map_config/test.ts b/tests/specs/import_map/import_map_config/test.ts
new file mode 100644
index 000000000..9b09e9953
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/test.ts
@@ -0,0 +1,6 @@
+import "moment";
+import "moment/other_file.ts";
+import "lodash";
+import "lodash/other_file.ts";
+import "https://www.unpkg.com/vue/dist/vue.runtime.esm.js";
+import "./scope/scoped.ts";
diff --git a/tests/specs/import_map/import_map_config/unmapped_bare_specifier.out b/tests/specs/import_map/import_map_config/unmapped_bare_specifier.out
new file mode 100644
index 000000000..6980fc16b
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/unmapped_bare_specifier.out
@@ -0,0 +1,6 @@
+error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "file://[WILDCARD]/unmapped_bare_specifier.ts"
+ at file://[WILDCARD]/unmapped_bare_specifier.ts:1:14
+
+await import("unmapped");
+^
+ at async file://[WILDCARD]/unmapped_bare_specifier.ts:1:1
diff --git a/tests/testdata/run/092_import_map_unmapped_bare_specifier.ts b/tests/specs/import_map/import_map_config/unmapped_bare_specifier.ts
index 87684430d..87684430d 100644
--- a/tests/testdata/run/092_import_map_unmapped_bare_specifier.ts
+++ b/tests/specs/import_map/import_map_config/unmapped_bare_specifier.ts
diff --git a/tests/specs/import_map/import_map_config/vue.ts b/tests/specs/import_map/import_map_config/vue.ts
new file mode 100644
index 000000000..76dbe1917
--- /dev/null
+++ b/tests/specs/import_map/import_map_config/vue.ts
@@ -0,0 +1 @@
+console.log("Hello from remapped Vue!");
diff --git a/tests/specs/info/ts_error/__test__.json b/tests/specs/info/ts_error/__test__.json
new file mode 100644
index 000000000..9ec9b6044
--- /dev/null
+++ b/tests/specs/info/ts_error/__test__.json
@@ -0,0 +1,4 @@
+{
+ "args": "info info_ts_error.ts",
+ "output": "info_ts_error.out"
+}
diff --git a/tests/specs/info/ts_error/info_ts_error.out b/tests/specs/info/ts_error/info_ts_error.out
new file mode 100644
index 000000000..d2e7aeb14
--- /dev/null
+++ b/tests/specs/info/ts_error/info_ts_error.out
@@ -0,0 +1,6 @@
+local: [WILDCARD]info_ts_error.ts
+type: TypeScript
+dependencies: 0 unique
+size: [WILDCARD]
+
+[WILDCARD]info_ts_error.ts ([WILDCARD])
diff --git a/tests/testdata/info/031_info_ts_error.ts b/tests/specs/info/ts_error/info_ts_error.ts
index 9b7492dbe..9b7492dbe 100644
--- a/tests/testdata/info/031_info_ts_error.ts
+++ b/tests/specs/info/ts_error/info_ts_error.ts
diff --git a/tests/specs/mod.rs b/tests/specs/mod.rs
new file mode 100644
index 000000000..831b94430
--- /dev/null
+++ b/tests/specs/mod.rs
@@ -0,0 +1,279 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::collections::HashSet;
+
+use deno_core::anyhow::Context;
+use deno_core::serde_json;
+use deno_terminal::colors;
+use serde::Deserialize;
+use test_util::tests_path;
+use test_util::PathRef;
+use test_util::TestContextBuilder;
+
+pub fn main() {
+ let maybe_filter = parse_cli_arg_filter();
+ let categories = filter(collect_tests(), maybe_filter.as_deref());
+ let total_tests = categories.iter().map(|c| c.tests.len()).sum::<usize>();
+ let mut failures = Vec::new();
+ let _http_guard = test_util::http_server();
+ // todo(dsherret): the output should be changed to be terse
+ // when it passes, but verbose on failure
+ for category in &categories {
+ eprintln!();
+ eprintln!(" {} {}", colors::green_bold("Running"), category.name);
+ for test in &category.tests {
+ eprintln!();
+ eprintln!("==== Starting {} ====", test.name);
+ let result = std::panic::catch_unwind(|| run_test(test));
+ let success = result.is_ok();
+ if !success {
+ failures.push(&test.name);
+ }
+ eprintln!(
+ "==== {} {} ====",
+ if success {
+ "Finished".to_string()
+ } else {
+ colors::red("^^FAILED^^").to_string()
+ },
+ test.name
+ );
+ }
+ }
+
+ eprintln!();
+ if !failures.is_empty() {
+ eprintln!("spec failures:");
+ for failure in &failures {
+ eprintln!(" {}", failure);
+ }
+ eprintln!();
+ panic!("{} failed of {}", failures.len(), total_tests);
+ }
+ eprintln!("{} tests passed", total_tests);
+}
+
+fn parse_cli_arg_filter() -> Option<String> {
+ let args: Vec<String> = std::env::args().collect();
+ let maybe_filter =
+ args.get(1).filter(|s| !s.starts_with('-') && !s.is_empty());
+ maybe_filter.cloned()
+}
+
+fn run_test(test: &Test) {
+ let metadata = &test.metadata;
+ let mut builder = TestContextBuilder::new();
+ let cwd = &test.cwd;
+
+ if test.metadata.temp_dir {
+ builder = builder.use_temp_cwd();
+ } else {
+ builder = builder.cwd(cwd.to_string_lossy());
+ }
+
+ if let Some(base) = &metadata.base {
+ match base.as_str() {
+ "npm" => {
+ builder = builder.add_npm_env_vars();
+ }
+ "jsr" => {
+ builder = builder.add_jsr_env_vars();
+ }
+ _ => panic!("Unknown test base: {}", base),
+ }
+ }
+
+ let context = builder.build();
+
+ if test.metadata.temp_dir {
+ // copy all the files in the cwd to a temp directory
+ // excluding the metadata and assertion files
+ let temp_dir = context.temp_dir().path();
+ let assertion_paths = test.resolve_test_and_assertion_files();
+ cwd.copy_to_recursive_with_exclusions(temp_dir, &assertion_paths);
+ }
+
+ for step in &metadata.steps {
+ if step.clean_deno_dir {
+ context.deno_dir().path().remove_dir_all();
+ }
+
+ let test_output_path = cwd.join(&step.output);
+ if !test_output_path.to_string_lossy().ends_with(".out") {
+ panic!(
+ "Use the .out extension for output files (invalid: {})",
+ test_output_path
+ );
+ }
+ let expected_output = test_output_path.read_to_string();
+ let command = context.new_command();
+ let command = match &step.args {
+ VecOrString::Vec(args) => command.args_vec(args),
+ VecOrString::String(text) => command.args(text),
+ };
+ let output = command.run();
+ output.assert_matches_text(expected_output);
+ output.assert_exit_code(step.exit_code);
+ }
+}
+
+#[derive(Clone, Deserialize)]
+#[serde(untagged)]
+enum VecOrString {
+ Vec(Vec<String>),
+ String(String),
+}
+
+#[derive(Clone, Deserialize)]
+#[serde(deny_unknown_fields, rename_all = "camelCase")]
+struct MultiTestMetaData {
+ /// Whether to copy all the non-assertion files in the current
+ /// test directory to a temporary directory before running the
+ /// steps.
+ #[serde(default)]
+ pub temp_dir: bool,
+ /// The base environment to use for the test.
+ #[serde(default)]
+ pub base: Option<String>,
+ pub steps: Vec<StepMetaData>,
+}
+
+#[derive(Clone, Deserialize)]
+#[serde(deny_unknown_fields, rename_all = "camelCase")]
+struct SingleTestMetaData {
+ #[serde(default)]
+ pub base: Option<String>,
+ #[serde(default)]
+ pub temp_dir: bool,
+ #[serde(flatten)]
+ pub step: StepMetaData,
+}
+
+impl SingleTestMetaData {
+ pub fn into_multi(self) -> MultiTestMetaData {
+ MultiTestMetaData {
+ base: self.base,
+ temp_dir: self.temp_dir,
+ steps: vec![self.step],
+ }
+ }
+}
+
+#[derive(Clone, Deserialize)]
+#[serde(deny_unknown_fields, rename_all = "camelCase")]
+struct StepMetaData {
+ /// Whether to clean the deno_dir before running the step.
+ #[serde(default)]
+ pub clean_deno_dir: bool,
+ pub args: VecOrString,
+ pub output: String,
+ #[serde(default)]
+ pub exit_code: i32,
+}
+
+#[derive(Clone)]
+struct Test {
+ pub name: String,
+ pub cwd: PathRef,
+ pub metadata: MultiTestMetaData,
+}
+
+impl Test {
+ pub fn resolve_test_and_assertion_files(&self) -> HashSet<PathRef> {
+ let mut result = HashSet::with_capacity(self.metadata.steps.len() + 1);
+ result.insert(self.cwd.join("__test__.json"));
+ result.extend(
+ self
+ .metadata
+ .steps
+ .iter()
+ .map(|step| self.cwd.join(&step.output)),
+ );
+ result
+ }
+}
+
+struct TestCategory {
+ pub name: String,
+ pub tests: Vec<Test>,
+}
+
+fn filter(
+ categories: Vec<TestCategory>,
+ maybe_filter: Option<&str>,
+) -> Vec<TestCategory> {
+ if categories.iter().all(|c| c.tests.is_empty()) {
+ panic!("no tests found");
+ }
+ match maybe_filter {
+ Some(filter) => categories
+ .into_iter()
+ .map(|mut c| {
+ c.tests.retain(|t| t.name.contains(filter));
+ c
+ })
+ .collect(),
+ None => categories,
+ }
+}
+
+fn collect_tests() -> Vec<TestCategory> {
+ let specs_dir = tests_path().join("specs");
+ let mut result = Vec::new();
+ for entry in specs_dir.read_dir() {
+ let entry = entry.unwrap();
+ let file_type = entry
+ .file_type()
+ .context(entry.path().to_string_lossy().to_string())
+ .unwrap();
+ if !file_type.is_dir() {
+ continue;
+ }
+
+ let mut category = TestCategory {
+ name: format!("specs::{}", entry.file_name().to_string_lossy()),
+ tests: Vec::new(),
+ };
+
+ let category_path = PathRef::new(entry.path());
+ for entry in category_path.read_dir() {
+ let entry = entry.unwrap();
+ let file_type = entry
+ .file_type()
+ .context(entry.path().to_string_lossy().to_string())
+ .unwrap();
+ if !file_type.is_dir() {
+ continue;
+ }
+
+ let test_dir = PathRef::new(entry.path());
+ let metadata_path = test_dir.join("__test__.json");
+ let metadata_value = metadata_path.read_jsonc_value();
+ // checking for "steps" leads to a more targeted error message
+ // instead of when deserializing an untagged enum
+ let metadata = if metadata_value
+ .as_object()
+ .and_then(|o| o.get("steps"))
+ .is_some()
+ {
+ serde_json::from_value::<MultiTestMetaData>(metadata_value)
+ } else {
+ serde_json::from_value::<SingleTestMetaData>(metadata_value)
+ .map(|s| s.into_multi())
+ }
+ .with_context(|| format!("Failed to parse {}", metadata_path))
+ .unwrap();
+ category.tests.push(Test {
+ name: format!(
+ "{}::{}",
+ category.name,
+ entry.file_name().to_string_lossy()
+ ),
+ cwd: test_dir,
+ metadata,
+ });
+ }
+ result.push(category);
+ }
+ result
+}
diff --git a/tests/specs/npm/conditional_exports/__test__.json b/tests/specs/npm/conditional_exports/__test__.json
new file mode 100644
index 000000000..72b058cbf
--- /dev/null
+++ b/tests/specs/npm/conditional_exports/__test__.json
@@ -0,0 +1,5 @@
+{
+ "base": "npm",
+ "args": "run --allow-read main.js",
+ "output": "main.out"
+}
diff --git a/tests/testdata/npm/conditional_exports/main.js b/tests/specs/npm/conditional_exports/main.js
index 52b78bc22..52b78bc22 100644
--- a/tests/testdata/npm/conditional_exports/main.js
+++ b/tests/specs/npm/conditional_exports/main.js
diff --git a/tests/testdata/npm/conditional_exports/main.out b/tests/specs/npm/conditional_exports/main.out
index dbd1b87fe..dbd1b87fe 100644
--- a/tests/testdata/npm/conditional_exports/main.out
+++ b/tests/specs/npm/conditional_exports/main.out
diff --git a/tests/specs/npm/conditional_exports_node_modules_dir/__test__.json b/tests/specs/npm/conditional_exports_node_modules_dir/__test__.json
new file mode 100644
index 000000000..09ea8b23b
--- /dev/null
+++ b/tests/specs/npm/conditional_exports_node_modules_dir/__test__.json
@@ -0,0 +1,6 @@
+{
+ "base": "npm",
+ "tempDir": true,
+ "args": "run --allow-read --node-modules-dir main.js",
+ "output": "main.out"
+}
diff --git a/tests/specs/npm/conditional_exports_node_modules_dir/main.js b/tests/specs/npm/conditional_exports_node_modules_dir/main.js
new file mode 100644
index 000000000..52b78bc22
--- /dev/null
+++ b/tests/specs/npm/conditional_exports_node_modules_dir/main.js
@@ -0,0 +1,15 @@
+import mod from "npm:@denotest/conditional-exports";
+import foo from "npm:@denotest/conditional-exports/foo.js";
+import client from "npm:@denotest/conditional-exports/client";
+import clientFoo from "npm:@denotest/conditional-exports/client/foo";
+import clientBar from "npm:@denotest/conditional-exports/client/bar";
+import clientM from "npm:@denotest/conditional-exports/client/m";
+import supportsESM from "npm:supports-esm";
+
+console.log(mod);
+console.log(foo);
+console.log(client);
+console.log(clientFoo);
+console.log(clientBar);
+console.log(clientM);
+console.log(supportsESM);
diff --git a/tests/testdata/npm/conditional_exports/main_node_modules.out b/tests/specs/npm/conditional_exports_node_modules_dir/main.out
index 460aec0f1..460aec0f1 100644
--- a/tests/testdata/npm/conditional_exports/main_node_modules.out
+++ b/tests/specs/npm/conditional_exports_node_modules_dir/main.out
diff --git a/tests/specs/npm/es_module/__test__.json b/tests/specs/npm/es_module/__test__.json
new file mode 100644
index 000000000..f720a2bc0
--- /dev/null
+++ b/tests/specs/npm/es_module/__test__.json
@@ -0,0 +1,22 @@
+{
+ "base": "npm",
+ "steps": [{
+ "args": "run --allow-read --allow-env main.js",
+ "output": "main.out"
+ }, {
+ "cleanDenoDir": true,
+ "args": "test --allow-read --allow-env test.js",
+ "output": "test.out"
+ }, {
+ "cleanDenoDir": true,
+ "args": [
+ "eval",
+ "import chalk from 'npm:chalk@5'; console.log(chalk.green('chalk esm loads'));"
+ ],
+ "output": "main.out"
+ }, {
+ "args": "bundle --quiet main.js",
+ "output": "bundle.out",
+ "exitCode": 1
+ }]
+}
diff --git a/tests/specs/npm/es_module/bundle.out b/tests/specs/npm/es_module/bundle.out
new file mode 100644
index 000000000..c749a236a
--- /dev/null
+++ b/tests/specs/npm/es_module/bundle.out
@@ -0,0 +1 @@
+error: npm specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: npm:/chalk@5.0.1
diff --git a/tests/testdata/npm/esm/main.js b/tests/specs/npm/es_module/main.js
index 0f1fa2590..0f1fa2590 100644
--- a/tests/testdata/npm/esm/main.js
+++ b/tests/specs/npm/es_module/main.js
diff --git a/tests/testdata/npm/esm/main.out b/tests/specs/npm/es_module/main.out
index 2010a5b73..2010a5b73 100644
--- a/tests/testdata/npm/esm/main.out
+++ b/tests/specs/npm/es_module/main.out
diff --git a/tests/testdata/npm/esm/test.js b/tests/specs/npm/es_module/test.js
index b9c91c715..b9c91c715 100644
--- a/tests/testdata/npm/esm/test.js
+++ b/tests/specs/npm/es_module/test.js
diff --git a/tests/testdata/npm/esm/test.out b/tests/specs/npm/es_module/test.out
index a87924424..0ed0fbd3c 100644
--- a/tests/testdata/npm/esm/test.out
+++ b/tests/specs/npm/es_module/test.out
@@ -1,6 +1,6 @@
Download http://localhost:4545/npm/registry/chalk
Download http://localhost:4545/npm/registry/chalk/chalk-5.0.1.tgz
-running 1 test from ./npm/esm/test.js
+running 1 test from ./test.js
test ...
------- output -------
test
diff --git a/tests/specs/run/redirect_javascript/__test__.json b/tests/specs/run/redirect_javascript/__test__.json
new file mode 100644
index 000000000..1fd0b4cb1
--- /dev/null
+++ b/tests/specs/run/redirect_javascript/__test__.json
@@ -0,0 +1,4 @@
+{
+ "args": "run --quiet --reload main.js",
+ "output": "main.out"
+}
diff --git a/tests/testdata/run/026_redirect_javascript.js b/tests/specs/run/redirect_javascript/main.js
index 226a6b622..226a6b622 100644
--- a/tests/testdata/run/026_redirect_javascript.js
+++ b/tests/specs/run/redirect_javascript/main.js
diff --git a/tests/testdata/run/026_redirect_javascript.js.out b/tests/specs/run/redirect_javascript/main.out
index 290864299..290864299 100644
--- a/tests/testdata/run/026_redirect_javascript.js.out
+++ b/tests/specs/run/redirect_javascript/main.out
diff --git a/tests/testdata/import_maps/import_map_remote.json b/tests/testdata/import_maps/import_map_remote.json
index 51f90f69c..190fc4f55 100644
--- a/tests/testdata/import_maps/import_map_remote.json
+++ b/tests/testdata/import_maps/import_map_remote.json
@@ -4,6 +4,7 @@
"moment/": "./moment/",
"lodash": "./lodash/lodash.ts",
"lodash/": "./lodash/",
- "https://www.unpkg.com/vue/dist/vue.runtime.esm.js": "./vue.ts"
+ "https://www.unpkg.com/vue/dist/vue.runtime.esm.js": "./vue.ts",
+ "print_hello": "./print_hello.ts"
}
}
diff --git a/tests/testdata/info/031_info_ts_error.out b/tests/testdata/info/031_info_ts_error.out
deleted file mode 100644
index 81edd0032..000000000
--- a/tests/testdata/info/031_info_ts_error.out
+++ /dev/null
@@ -1,6 +0,0 @@
-local: [WILDCARD]031_info_ts_error.ts
-type: TypeScript
-dependencies: 0 unique
-size: [WILDCARD]
-
-[WILDCARD]031_info_ts_error.ts ([WILDCARD])
diff --git a/tests/testdata/run/092_import_map_unmapped_bare_specifier.ts.out b/tests/testdata/run/092_import_map_unmapped_bare_specifier.ts.out
deleted file mode 100644
index 7f35b8b4f..000000000
--- a/tests/testdata/run/092_import_map_unmapped_bare_specifier.ts.out
+++ /dev/null
@@ -1,6 +0,0 @@
-error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts"
- at file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts:1:14
-
-await import("unmapped");
-^
- at async file://[WILDCARD]/092_import_map_unmapped_bare_specifier.ts:1:1
diff --git a/tests/util/server/Cargo.toml b/tests/util/server/Cargo.toml
index fbec0e1aa..15a182a78 100644
--- a/tests/util/server/Cargo.toml
+++ b/tests/util/server/Cargo.toml
@@ -30,6 +30,7 @@ http.workspace = true
http-body-util.workspace = true
hyper.workspace = true
hyper-util.workspace = true
+jsonc-parser.workspace = true
lazy-regex.workspace = true
libc.workspace = true
lsp-types.workspace = true
diff --git a/tests/util/server/src/fs.rs b/tests/util/server/src/fs.rs
index d99572b06..fb3e36ab6 100644
--- a/tests/util/server/src/fs.rs
+++ b/tests/util/server/src/fs.rs
@@ -2,6 +2,7 @@
use pretty_assertions::assert_eq;
use std::borrow::Cow;
+use std::collections::HashSet;
use std::ffi::OsStr;
use std::fs;
use std::fs::OpenOptions;
@@ -21,7 +22,7 @@ use crate::testdata_path;
/// Represents a path on the file system, which can be used
/// to perform specific actions.
-#[derive(Clone, Debug, Default, PartialEq, Eq)]
+#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct PathRef(PathBuf);
impl AsRef<Path> for PathRef {
@@ -124,12 +125,29 @@ impl PathRef {
fs::read(self).with_context(|| format!("Could not read file: {}", self))
}
+ #[track_caller]
pub fn read_json<TValue: DeserializeOwned>(&self) -> TValue {
- serde_json::from_str(&self.read_to_string()).unwrap()
+ serde_json::from_str(&self.read_to_string())
+ .with_context(|| format!("Failed deserializing: {}", self))
+ .unwrap()
}
+ #[track_caller]
pub fn read_json_value(&self) -> serde_json::Value {
- serde_json::from_str(&self.read_to_string()).unwrap()
+ serde_json::from_str(&self.read_to_string())
+ .with_context(|| format!("Failed deserializing: {}", self))
+ .unwrap()
+ }
+
+ #[track_caller]
+ pub fn read_jsonc_value(&self) -> serde_json::Value {
+ jsonc_parser::parse_to_serde_value(
+ &self.read_to_string(),
+ &Default::default(),
+ )
+ .with_context(|| format!("Failed to parse {}", self))
+ .unwrap()
+ .expect("Found no value.")
}
pub fn rename(&self, to: impl AsRef<Path>) {
@@ -204,6 +222,14 @@ impl PathRef {
///
/// Note: Does not handle symlinks.
pub fn copy_to_recursive(&self, to: &PathRef) {
+ self.copy_to_recursive_with_exclusions(to, &HashSet::new())
+ }
+
+ pub fn copy_to_recursive_with_exclusions(
+ &self,
+ to: &PathRef,
+ file_exclusions: &HashSet<PathRef>,
+ ) {
to.create_dir_all();
let read_dir = self.read_dir();
@@ -215,7 +241,7 @@ impl PathRef {
if file_type.is_dir() {
new_from.copy_to_recursive(&new_to);
- } else if file_type.is_file() {
+ } else if file_type.is_file() && !file_exclusions.contains(&new_from) {
new_from.copy(&new_to);
}
}
diff --git a/tools/copyright_checker.js b/tools/copyright_checker.js
index 7337c0d84..86289b9d4 100644
--- a/tools/copyright_checker.js
+++ b/tools/copyright_checker.js
@@ -24,6 +24,7 @@ export async function checkCopyright() {
"*.js",
"*.ts",
":!:.github/mtime_cache/action.js",
+ ":!:tests/specs/**",
":!:tests/testdata/**",
":!:cli/bench/testdata/**",
":!:cli/tsc/dts/**",