1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-27 17:49:08 -05:00
denoland-deno/tests/integration/compile_tests.rs
David Sherret 147411e64b
feat: npm workspace and better Deno workspace support (#24334)
Adds much better support for the unstable Deno workspaces as well as
support for npm workspaces. npm workspaces is still lacking in that we
only install packages into the root node_modules folder. We'll make it
smarter over time in order for it to figure out when to add node_modules
folders within packages.

This includes a breaking change in config file resolution where we stop
searching for config files on the first found package.json unless it's
in a workspace. For the previous behaviour, the root deno.json needs to
be updated to be a workspace by adding `"workspace":
["./path-to-pkg-json-folder-goes-here"]`. See details in
https://github.com/denoland/deno_config/pull/66

Closes #24340
Closes #24159
Closes #24161
Closes #22020
Closes #18546
Closes #16106
Closes #24160
2024-07-04 00:54:33 +00:00

1287 lines
32 KiB
Rust

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::serde_json;
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(),
"../../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())
.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:///[WILDLINE]/main.ts\nCompile file:///[WILDLINE]/main.ts to binary[WILDLINE]\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",
copy_temp_dir: Some("compile/npm_fs"),
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",
copy_temp_dir: None,
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",
copy_temp_dir: None,
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",
copy_temp_dir: None,
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",
copy_temp_dir: Some("compile/vfs_implicit_read_permission"),
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",
copy_temp_dir: None,
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",
copy_temp_dir: None,
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",
copy_temp_dir: None,
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,
copy_temp_dir: Option<&'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 builder = TestContextBuilder::for_npm();
let context = match opts.copy_temp_dir {
Some(copy_temp_dir) => builder.use_copy_temp_dir(copy_temp_dir).build(),
None => builder.use_temp_cwd().build(),
};
let temp_dir = context.temp_dir();
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(opts.input_specifier.to_string());
// 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() {
// this code is using a canonicalized temp dir because otherwise
// it fails on the Windows CI because Deno makes the root directory
// a common ancestor of the symlinked temp dir and the canonicalized
// temp dir, which causes the warnings to not be surfaced
#[allow(deprecated)]
let context = TestContextBuilder::for_npm()
.use_canonicalized_temp_dir()
.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");
let symlink_target_dir = temp_dir.path().join("some_folder");
project_dir.join("node_modules").create_dir_all();
symlink_target_dir.create_dir_all();
let symlink_target_file = temp_dir.path().join("target.txt");
symlink_target_file.write("5");
let symlink_dir = project_dir.join("node_modules").join("symlink_dir");
// create a symlink in the node_modules directory that points to a folder outside the project
temp_dir.symlink_dir(&symlink_target_dir, &symlink_dir);
// 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!(symlink_dir.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.
symlink_dir.remove_dir_all();
// create a symlink in the node_modules directory that points to a file in the cwd
temp_dir.symlink_file(
&symlink_target_file,
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:4260/@denotest/esm-basic
Download http://localhost:4260/@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",
"--unstable-temporal",
"./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 {}\nObject [Temporal] {}\n");
}
#[test]
fn granular_unstable_features_config_file() {
let context = TestContextBuilder::new().use_temp_cwd().build();
let dir = context.temp_dir();
testdata_path()
.join("compile/unstable_features.ts")
.copy(&dir.path().join("unstable_features.ts"));
let exe = if cfg!(windows) {
dir.path().join("app.exe")
} else {
dir.path().join("app")
};
dir.write(
"deno.json",
serde_json::to_string_pretty(&serde_json::json!({
"unstable": ["kv", "temporal"]
}))
.unwrap(),
);
let output = context
.new_command()
.args_vec([
"compile",
"--config",
&dir.path().join("deno.json").to_string(),
"--output",
&exe.to_string_lossy(),
"./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 {}\nObject [Temporal] {}\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]",
);
}
#[test]
fn standalone_config_file_respects_compiler_options() {
let context = TestContextBuilder::new().build();
let dir = context.temp_dir();
let exe = if cfg!(windows) {
dir.path().join("compiler_options.exe")
} else {
dir.path().join("compiler_options")
};
context
.new_command()
.args_vec([
"compile",
"--allow-read",
"--config",
"compile/compiler_options/deno.json",
"--output",
&exe.to_string_lossy(),
"./compile/compiler_options/main.ts",
])
.run()
.skip_output_check()
.assert_exit_code(0);
let output = context.new_command().name(&exe).run();
output.assert_exit_code(0);
output.assert_matches_text("[WILDCARD]C.test() called[WILDCARD]");
}
#[test]
fn standalone_jsr_dynamic_import() {
let context = TestContextBuilder::for_jsr().build();
let dir = context.temp_dir();
let exe = if cfg!(windows) {
dir.path().join("jsr_dynamic_import.exe")
} else {
dir.path().join("jsr_dynamic_import")
};
context
.new_command()
.args_vec([
"compile",
"--output",
&exe.to_string_lossy(),
"./compile/jsr_dynamic_import/main.ts",
])
.run()
.skip_output_check()
.assert_exit_code(0);
let output = context.new_command().name(&exe).run();
output.assert_exit_code(0);
output.assert_matches_text("Hello world\n");
}