/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
libs/cli_parser/src/tests_full.rs
9 768 строк
230 KB
Bartek Iwańczuk
refactor: remove clap from the tree (#36489)
08 авг 2026, 01:24
Не верифицирован
08 авг 2026, 01:24
41b646e
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. #![allow( clippy::useless_vec, reason = "svec! macro creates Vec<String> for test inputs" )] // Ported from cli/args/flags.rs `mod tests` (the clap parity contract) to run // against the zero-cost parser via `convert::flags_from_vec`. A few tests that // need CLI-side state (cwd resolution, env vars, the bare-run fast path) are // skipped inline and stay in cli/args/flags.rs. use std::net::SocketAddr; use std::num::NonZeroU8; use std::num::NonZeroU32; use std::num::NonZeroUsize; use deno_semver::package::PackageReq; use pretty_assertions::assert_eq; use crate::CliErrorKind; use crate::convert::escape_and_split_commas; use crate::convert::flags_from_vec; use crate::convert::set_test_node_options; use crate::flags::*; /// Creates a Vec<String> from string literals. macro_rules! svec { ($($x:expr),* $(,)?) => { vec![$($x.to_string()),*] }; } #[test] fn global_flags() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "--log-level", "debug", "--quiet", "run", "script.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string() )), log_level: Some(Level::Error), code_cache_enabled: true, ..Flags::default() } ); #[rustfmt::skip] let r2 = flags_from_vec(svec!["deno", "run", "--log-level", "debug", "--quiet", "script.ts"]); let flags2 = r2.unwrap(); assert_eq!(flags2, flags); } #[test] fn crlf_shebang_arg() { // A script saved with CRLF line endings whose first line is // `#!/usr/bin/env -S deno run --allow-net` is invoked by the kernel as // roughly `deno run --allow-net\r script.ts`. The stray `\r` on the last // shebang token must not break flag parsing. let r = flags_from_vec(svec!["deno", "run", "--allow-net\r", "script.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string() )), permissions: PermissionFlags { allow_net: Some(vec![]), ..PermissionFlags::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn upgrade() { let r = flags_from_vec(svec!["deno", "upgrade", "--dry-run", "--force"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: true, dry_run: true, canary: false, no_delta: false, release_candidate: false, version: None, output: None, version_or_hash_or_channel: None, checksum: None, pr: None, branch: None, }), ..Flags::default() } ); } #[test] fn upgrade_with_output_flag() { let r = flags_from_vec(svec!["deno", "upgrade", "--output", "example.txt"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: false, dry_run: false, canary: false, no_delta: false, release_candidate: false, version: None, output: Some(String::from("example.txt")), version_or_hash_or_channel: None, checksum: None, pr: None, branch: None, }), ..Flags::default() } ); } #[test] fn version() { let r = flags_from_vec(svec!["deno", "--version"]); assert_eq!(r.unwrap_err().kind, CliErrorKind::DisplayVersion); let r = flags_from_vec(svec!["deno", "-V"]); assert_eq!(r.unwrap_err().kind, CliErrorKind::DisplayVersion); } #[test] fn run_reload() { let r = flags_from_vec(svec!["deno", "run", "-r", "script.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string() )), reload: true, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_watch() { let r = flags_from_vec(svec!["deno", "run", "--watch", "script.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![], no_clear_screen: false, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "--watch", "--no-clear-screen", "script.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![], no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--watch-hmr", "--no-clear-screen", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: true, paths: vec![], no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--unstable-hmr", "--no-clear-screen", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: true, paths: vec![], no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--watch-hmr=foo.txt", "--no-clear-screen", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: true, paths: vec![String::from("foo.txt")], no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "--hmr", "--watch", "script.ts"]); assert!(r.is_err()); } #[test] fn watch_subcommand() { // `deno watch script.ts` is an alias for `deno run --watch-hmr script.ts`. let r = flags_from_vec(svec!["deno", "watch", "script.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: true, paths: vec![], no_clear_screen: false, exclude: vec![], }), ..Flags::default() } ); // Additional watched paths and watch options are still respected. let r = flags_from_vec(svec![ "deno", "watch", "--watch-hmr=foo.txt", "--no-clear-screen", "--watch-exclude=bar.txt", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: true, paths: vec![String::from("foo.txt")], no_clear_screen: true, exclude: vec![String::from("bar.txt")], }), ..Flags::default() } ); // `--watch-exclude` is honored even without an explicit `--watch-hmr` flag. let r = flags_from_vec(svec![ "deno", "watch", "--watch-exclude=bar.txt", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: true, paths: vec![], no_clear_screen: false, exclude: vec![String::from("bar.txt")], }), ..Flags::default() } ); // Reading from stdin while watching is not supported. let r = flags_from_vec(svec!["deno", "watch", "-"]); assert!(r.is_err()); } #[test] fn run_watch_with_external() { let r = flags_from_vec(svec!["deno", "--watch=file1,file2", "script.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("file1"), String::from("file2")], no_clear_screen: false, exclude: vec![], }), ..Flags::default() } ); } #[test] fn run_watch_with_no_clear_screen() { let r = flags_from_vec(svec![ "deno", "run", "--watch", "--no-clear-screen", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![], no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); } #[test] fn run_watch_with_excluded_paths() { let r = flags_from_vec(svec!( "deno", "--watch", "--watch-exclude=foo", "script.ts" )); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![], no_clear_screen: false, exclude: vec![String::from("foo")], }), ..Flags::default() } ); let r = flags_from_vec(svec!( "deno", "run", "--watch=foo", "--watch-exclude=bar", "script.ts" )); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("foo")], no_clear_screen: false, exclude: vec![String::from("bar")], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--watch", "--watch-exclude=foo,bar", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![], no_clear_screen: false, exclude: vec![String::from("foo"), String::from("bar")], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "--watch=foo,bar", "--watch-exclude=baz,qux", "script.ts" ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("foo"), String::from("bar")], no_clear_screen: false, exclude: vec![String::from("baz"), String::from("qux"),], }), ..Flags::default() } ); } #[test] fn run_watch_with_stdin_is_error() { // `--watch` and `--watch-hmr` cannot be combined with reading from stdin, // previously `--watch-hmr -` panicked and `--watch -` was silently ignored. for watch_flag in ["--watch", "--watch-hmr", "--unstable-hmr"] { let r = flags_from_vec(svec!["deno", "run", watch_flag, "-"]); assert!(r.is_err(), "expected conflict for {watch_flag}"); } // a regular script with the same flags must still parse fine let r = flags_from_vec(svec!["deno", "run", "--watch-hmr", "script.ts"]); assert!(r.is_ok()); } #[test] fn run_reload_allow_write() { let r = flags_from_vec(svec!["deno", "run", "-r", "--allow-write", "script.ts"]); assert_eq!( r.unwrap(), Flags { reload: true, subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string() )), permissions: PermissionFlags { allow_write: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_coverage() { let r = flags_from_vec(svec!["deno", "run", "--coverage=foo", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: false, coverage_dir: Some("foo".to_string()), print_task_list: false, }), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_v8_flags() { let r = flags_from_vec(svec!["deno", "run", "--v8-flags=--help"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default("_".to_string())), v8_flags: svec!["--help"], code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--v8-flags=--expose-gc,--gc-stats=1", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), v8_flags: svec!["--expose-gc", "--gc-stats=1"], code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "--v8-flags=--expose-gc"]); assert!(r.is_ok()); } #[test] fn serve_flags() { let r = flags_from_vec(svec!["deno", "serve", "main.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Serve(ServeFlags::new_default( "main.ts".to_string(), 8000, "0.0.0.0" )), permissions: PermissionFlags { allow_net: None, ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "serve", "--port", "5000", "main.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Serve(ServeFlags::new_default( "main.ts".to_string(), 5000, "0.0.0.0" )), permissions: PermissionFlags { allow_net: None, ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "serve", "--port", "5000", "--allow-net=example.com", "main.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Serve(ServeFlags::new_default( "main.ts".to_string(), 5000, "0.0.0.0" )), permissions: PermissionFlags { allow_net: Some(vec!["example.com".to_string(),]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "serve", "--port", "5000", "--allow-net", "main.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Serve(ServeFlags::new_default( "main.ts".to_string(), 5000, "0.0.0.0" )), permissions: PermissionFlags { allow_net: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn has_permission() { let r = flags_from_vec(svec!["deno", "--allow-read", "x.ts"]); assert_eq!(r.unwrap().has_permission(), true); let r = flags_from_vec(svec!["deno", "run", "--deny-read", "x.ts"]); assert_eq!(r.unwrap().has_permission(), true); let r = flags_from_vec(svec!["deno", "run", "x.ts"]); assert_eq!(r.unwrap().has_permission(), false); } #[test] fn has_permission_in_argv() { let r = flags_from_vec(svec!["deno", "run", "x.ts", "--allow-read"]); assert_eq!(r.unwrap().has_permission_in_argv(), true); let r = flags_from_vec(svec!["deno", "x.ts", "--deny-read"]); assert_eq!(r.unwrap().has_permission_in_argv(), true); let r = flags_from_vec(svec!["deno", "run", "x.ts"]); assert_eq!(r.unwrap().has_permission_in_argv(), false); } #[test] fn script_args() { let r = flags_from_vec(svec![ "deno", "run", "--allow-net", "gist.ts", "--title", "X" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "gist.ts".to_string() )), argv: svec!["--title", "X"], permissions: PermissionFlags { allow_net: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_all() { let r = flags_from_vec(svec!["deno", "run", "--allow-all", "gist.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "gist.ts".to_string() )), permissions: PermissionFlags { allow_all: true, ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_read() { let r = flags_from_vec(svec!["deno", "run", "--allow-read", "gist.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "gist.ts".to_string() )), permissions: PermissionFlags { allow_read: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn short_permission_flags() { let r = flags_from_vec(svec!["deno", "run", "-RNESWI", "gist.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "gist.ts".to_string() )), permissions: PermissionFlags { allow_read: Some(vec![]), allow_write: Some(vec![]), allow_env: Some(vec![]), allow_import: Some(vec![]), allow_net: Some(vec![]), allow_sys: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_read() { let r = flags_from_vec(svec!["deno", "--deny-read", "gist.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "gist.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), permissions: PermissionFlags { deny_read: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn double_hyphen() { // notice that flags passed after double dash will not // be parsed to Flags but instead forwarded to // script args as Deno.args let r = flags_from_vec(svec![ "deno", "run", "--allow-write", "script.ts", "--", "-D", "--allow-net" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), argv: svec!["--", "-D", "--allow-net"], permissions: PermissionFlags { allow_write: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn fmt() { let r = flags_from_vec(svec!["deno", "fmt", "script_1.ts", "script_2.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec!["script_1.ts".to_string(), "script_2.ts".to_string()], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "fmt", "--permit-no-files", "--check"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: true, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: true, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "fmt"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "fmt", "--watch"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), watch: Some(Default::default()), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "fmt", "--watch", "--no-clear-screen", "--unstable-css", "--unstable-html", "--unstable-component", "--unstable-yaml", "--unstable-sql" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: true, unstable_sql: true, }), watch: Some(WatchFlagsWithPaths { paths: vec![], hmr: false, no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "fmt", "--check", "--watch", "foo.ts", "--ignore=bar.js" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: true, fail_fast: false, files: FileFlags { include: vec!["foo.ts".to_string()], ignore: vec!["bar.js".to_string()], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), watch: Some(Default::default()), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "fmt", "--config", "deno.jsonc"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), config_flag: ConfigFlag::Path("deno.jsonc".to_string()), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "fmt", "--config", "deno.jsonc", "--watch", "foo.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec!["foo.ts".to_string()], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), config_flag: ConfigFlag::Path("deno.jsonc".to_string()), watch: Some(Default::default()), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "fmt", "--use-tabs", "--line-width", "60", "--indent-width", "4", "--single-quote", "--prose-wrap", "never", "--no-semicolons", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false, use_tabs: Some(true), line_width: Some(NonZeroU32::new(60).unwrap()), indent_width: Some(NonZeroU8::new(4).unwrap()), single_quote: Some(true), prose_wrap: Some("never".to_string()), no_semicolons: Some(true), no_editorconfig: false, unstable_component: false, unstable_sql: false, }), ..Flags::default() } ); // try providing =false to the booleans let r = flags_from_vec(svec![ "deno", "fmt", "--use-tabs=false", "--single-quote=false", "--no-semicolons=false", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false, use_tabs: Some(false), line_width: None, indent_width: None, single_quote: Some(false), prose_wrap: None, no_semicolons: Some(false), no_editorconfig: false, unstable_component: false, unstable_sql: false, }), ..Flags::default() } ); // --no-editorconfig opts out of reading .editorconfig let r = flags_from_vec(svec!["deno", "fmt", "--no-editorconfig"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: true, unstable_component: false, unstable_sql: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html", "./**"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { check: false, fail_fast: false, files: FileFlags { include: vec!["./**".to_string()], ignore: vec![], }, permit_no_files: false, use_tabs: None, line_width: None, indent_width: None, single_quote: None, prose_wrap: None, no_semicolons: None, no_editorconfig: false, unstable_component: false, unstable_sql: false, }), ext: Some("html".to_string()), ..Flags::default() } ); } #[test] fn lint() { let r = flags_from_vec(svec!["deno", "lint", "script_1.ts", "script_2.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec!["script_1.ts".to_string(), "script_2.ts".to_string(),], ignore: vec![], }, fix: false, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: false, compact: false, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "lint", "--permit-no-files", "--allow-import", "--watch", "script_1.ts", "script_2.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec!["script_1.ts".to_string(), "script_2.ts".to_string()], ignore: vec![], }, fix: false, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: true, json: false, compact: false, }), permissions: PermissionFlags { allow_import: Some(vec![]), ..Default::default() }, watch: Some(Default::default()), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "lint", "--watch", "--no-clear-screen", "script_1.ts", "script_2.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec!["script_1.ts".to_string(), "script_2.ts".to_string()], ignore: vec![], }, fix: false, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: false, compact: false, }), watch: Some(WatchFlagsWithPaths { paths: vec![], hmr: false, no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "lint", "--fix", "--ignore=script_1.ts,script_2.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec![], ignore: vec!["script_1.ts".to_string(), "script_2.ts".to_string()], }, fix: true, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: false, compact: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "lint", "--rules"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec![], ignore: vec![], }, fix: false, rules: true, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: false, compact: false, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "lint", "--rules", "--rules-tags=recommended" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec![], ignore: vec![], }, fix: false, rules: true, maybe_rules_tags: Some(svec!["recommended"]), maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: false, compact: false, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "lint", "--rules-tags=", "--rules-include=ban-untagged-todo,no-undef", "--rules-exclude=no-const-assign" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec![], ignore: vec![], }, fix: false, rules: false, maybe_rules_tags: Some(svec![""]), maybe_rules_include: Some(svec!["ban-untagged-todo", "no-undef"]), maybe_rules_exclude: Some(svec!["no-const-assign"]), permit_no_files: false, json: false, compact: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "lint", "--json", "script_1.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec!["script_1.ts".to_string()], ignore: vec![], }, fix: false, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: true, compact: false, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "lint", "--config", "Deno.jsonc", "--json", "script_1.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec!["script_1.ts".to_string()], ignore: vec![], }, fix: false, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: true, compact: false, }), config_flag: ConfigFlag::Path("Deno.jsonc".to_string()), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "lint", "--config", "Deno.jsonc", "--compact", "script_1.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { files: FileFlags { include: vec!["script_1.ts".to_string()], ignore: vec![], }, fix: false, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, permit_no_files: false, json: false, compact: true, }), config_flag: ConfigFlag::Path("Deno.jsonc".to_string()), ..Flags::default() } ); } #[test] fn types() { let r = flags_from_vec(svec!["deno", "types"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Types, ..Flags::default() } ); } #[test] fn cache() { let r = flags_from_vec(svec!["deno", "cache", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Cache(CacheFlags { files: svec!["script.ts"], }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "cache", "--env-file", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Cache(CacheFlags { files: svec!["script.ts"], }), env_file: Some(svec![".env"]), ..Flags::default() } ); } #[test] fn check() { let r = flags_from_vec(svec!["deno", "check", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["script.ts"], doc: false, doc_only: false, check_js: false, }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "check"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["."], doc: false, doc_only: false, check_js: false, }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "check", "--doc", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["script.ts"], doc: true, doc_only: false, check_js: false, }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "check", "--doc-only", "markdown.md"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["markdown.md"], doc: false, doc_only: true, check_js: false, }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); // `--doc` and `--doc-only` are mutually exclusive let r = flags_from_vec(svec!["deno", "check", "--doc", "--doc-only", "script.ts"]); assert!(r.is_err()); for all_flag in ["--remote", "--all"] { let r = flags_from_vec(svec!["deno", "check", all_flag, "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["script.ts"], doc: false, doc_only: false, check_js: false, }), type_check_mode: TypeCheckMode::All, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "check", all_flag, "--no-remote", "script.ts" ]); assert!(r.is_err()); } let r = flags_from_vec(svec!["deno", "check", "--check-js", "script.js"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["script.js"], doc: false, doc_only: false, check_js: true, }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "check", "--desktop", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["script.ts"], doc: false, doc_only: false, check_js: false, }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, internal: InternalFlags { is_desktop: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn info() { let r = flags_from_vec(svec!["deno", "info", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { json: false, file: Some("script.ts".to_string()), }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "info", "--reload", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { json: false, file: Some("script.ts".to_string()), }), reload: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "info", "--json", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { json: true, file: Some("script.ts".to_string()), }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "info"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { json: false, file: None }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "info", "--json"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { json: true, file: None }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "info", "--no-npm", "--no-remote", "--config", "tsconfig.json" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { json: false, file: None }), config_flag: ConfigFlag::Path("tsconfig.json".to_owned()), no_npm: true, no_remote: true, ..Flags::default() } ); } #[test] fn tsconfig() { let r = flags_from_vec(svec!["deno", "run", "-c", "tsconfig.json", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), config_flag: ConfigFlag::Path("tsconfig.json".to_owned()), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn eval() { let r = flags_from_vec(svec!["deno", "eval", "'console.log(\"hello\")'"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Eval(EvalFlags { print: false, code: "'console.log(\"hello\")'".to_string(), }), permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn eval_p() { let r = flags_from_vec(svec!["deno", "eval", "-p", "1+2"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Eval(EvalFlags { print: true, code: "1+2".to_string(), }), permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn eval_typescript() { let r = flags_from_vec(svec![ "deno", "eval", "--ext=ts", "'console.log(\"hello\")'" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Eval(EvalFlags { print: false, code: "'console.log(\"hello\")'".to_string(), }), permissions: PermissionFlags { allow_all: true, ..Default::default() }, ext: Some("ts".to_string()), ..Flags::default() } ); } #[test] fn eval_with_flags() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "eval", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--env=.example.env", "42"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Eval(EvalFlags { print: false, code: "42".to_string(), }), import_map_path: Some("import_map.json".to_string()), no_remote: true, config_flag: ConfigFlag::Path("tsconfig.json".to_owned()), type_check_mode: TypeCheckMode::None, reload: true, lock: Some(String::from("lock.json")), ca_data: Some(CaData::File("example.crt".to_string())), cached_only: true, location: Some(Url::parse("https://foo/").unwrap()), v8_flags: svec!["--help", "--random-seed=1"], seed: Some(1), inspect: Some("127.0.0.1:9229".parse().unwrap()), permissions: PermissionFlags { allow_all: true, ..Default::default() }, env_file: Some(vec![".example.env".to_owned()]), ..Flags::default() } ); } #[test] fn eval_args() { let r = flags_from_vec(svec![ "deno", "eval", "console.log(Deno.args)", "arg1", "arg2" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Eval(EvalFlags { print: false, code: "console.log(Deno.args)".to_string(), }), argv: svec!["arg1", "arg2"], permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn repl() { let r = flags_from_vec(svec!["deno"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None, is_default_command: true, json: false, }), unsafely_ignore_certificate_errors: None, permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn repl_trace_ops() { // Lightly test this undocumented flag let r = flags_from_vec(svec!["deno", "repl", "--trace-ops"]); assert_eq!(r.unwrap().trace_ops, Some(vec![])); let r = flags_from_vec(svec!["deno", "repl", "--trace-ops=http,websocket"]); assert_eq!( r.unwrap().trace_ops, Some(vec!["http".to_string(), "websocket".to_string()]) ); } #[test] fn repl_with_flags() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None, is_default_command: false, json: false, }), import_map_path: Some("import_map.json".to_string()), no_remote: true, config_flag: ConfigFlag::Path("tsconfig.json".to_owned()), type_check_mode: TypeCheckMode::None, reload: true, lock: Some(String::from("lock.json")), ca_data: Some(CaData::File("example.crt".to_string())), cached_only: true, location: Some(Url::parse("https://foo/").unwrap()), v8_flags: svec!["--help", "--random-seed=1"], seed: Some(1), inspect: Some("127.0.0.1:9229".parse().unwrap()), permissions: PermissionFlags { allow_all: true, ..Default::default() }, env_file: Some(vec![".example.env".to_owned()]), unsafely_ignore_certificate_errors: Some(vec![]), ..Flags::default() } ); } #[test] fn repl_with_eval_flag() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "repl", "--allow-write", "--eval", "console.log('hello');"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: Some("console.log('hello');".to_string()), is_default_command: false, json: false, }), permissions: PermissionFlags { allow_write: Some(vec![]), ..Default::default() }, ..Flags::default() } ); } #[test] fn repl_with_eval_file_flag() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "repl", "--eval-file=./a.js,./b.ts,https://docs.deno.com/hello_world.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: Some(vec![ "./a.js".to_string(), "./b.ts".to_string(), "https://docs.deno.com/hello_world.ts".to_string() ]), eval: None, is_default_command: false, json: false, }), ..Flags::default() } ); } #[test] fn repl_with_eval_file_flag_no_equals() { // Test without equals sign (for hashbang usage) #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "repl", "--eval-file", "./script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: Some(vec!["./script.ts".to_string()]), eval: None, is_default_command: false, json: false, }), ..Flags::default() } ); } #[test] fn repl_with_eval_file_flag_multiple() { // Test multiple --eval-file flags #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "repl", "--eval-file", "./a.ts", "--eval-file", "./b.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: Some(vec!["./a.ts".to_string(), "./b.ts".to_string()]), eval: None, is_default_command: false, json: false, }), ..Flags::default() } ); } #[test] fn allow_read_allowlist() { #[allow( clippy::disallowed_methods, reason = "only building an argv string; no fs access" )] let temp_dir = std::env::temp_dir().to_string_lossy().into_owned(); let r = flags_from_vec(svec![ "deno", "run", format!("--allow-read=.,{}", temp_dir), "script.ts" ]); assert_eq!( r.unwrap(), Flags { permissions: PermissionFlags { allow_read: Some(vec![String::from("."), temp_dir]), ..Default::default() }, subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_read_denylist() { #[allow( clippy::disallowed_methods, reason = "only building an argv string; no fs access" )] let temp_dir = std::env::temp_dir().to_string_lossy().into_owned(); let r = flags_from_vec(svec![ "deno", "run", format!("--deny-read=.,{}", temp_dir), "script.ts" ]); assert_eq!( r.unwrap(), Flags { permissions: PermissionFlags { deny_read: Some(vec![String::from("."), temp_dir]), ..Default::default() }, subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn ignore_read_ignorelist() { let r = flags_from_vec(svec![ "deno", "run", "--ignore-read=something.txt", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { ignore_read: Some(svec!["something.txt"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn ignore_read_ignorelist_multiple() { let r = flags_from_vec(svec![ "deno", "run", "--ignore-read=something.txt", "--ignore-read=something2.txt", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { ignore_read: Some(svec!["something.txt", "something2.txt"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn ignore_read_ignorelist_comma_separated() { let r = flags_from_vec(svec![ "deno", "run", "--ignore-read=something.txt,something2.txt", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { ignore_read: Some(svec!["something.txt", "something2.txt"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_write_allowlist() { #[allow( clippy::disallowed_methods, reason = "only building an argv string; no fs access" )] let temp_dir = std::env::temp_dir().to_string_lossy().into_owned(); let r = flags_from_vec(svec![ "deno", "run", format!("--allow-write=.,{}", temp_dir), "script.ts" ]); assert_eq!( r.unwrap(), Flags { permissions: PermissionFlags { allow_write: Some(vec![String::from("."), temp_dir]), ..Default::default() }, subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_write_denylist() { #[allow( clippy::disallowed_methods, reason = "only building an argv string; no fs access" )] let temp_dir = std::env::temp_dir().to_string_lossy().into_owned(); let r = flags_from_vec(svec![ "deno", "run", format!("--deny-write=.,{}", temp_dir), "script.ts" ]); assert_eq!( r.unwrap(), Flags { permissions: PermissionFlags { deny_write: Some(vec![String::from("."), temp_dir]), ..Default::default() }, subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_net_allowlist() { let r = flags_from_vec(svec!["deno", "run", "--allow-net=127.0.0.1", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_net: Some(svec!["127.0.0.1"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_net_denylist() { let r = flags_from_vec(svec!["deno", "--deny-net=127.0.0.1", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), permissions: PermissionFlags { deny_net: Some(svec!["127.0.0.1"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_env_allowlist() { let r = flags_from_vec(svec!["deno", "run", "--allow-env=HOME", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_env: Some(svec!["HOME"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_env_denylist() { let r = flags_from_vec(svec!["deno", "run", "--deny-env=HOME", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { deny_env: Some(svec!["HOME"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn ignore_env_ignorelist() { let r = flags_from_vec(svec!["deno", "run", "--ignore-env=HOME", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { ignore_env: Some(svec!["HOME"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_env_allowlist_multiple() { let r = flags_from_vec(svec!["deno", "run", "--allow-env=HOME,PATH", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_env: Some(svec!["HOME", "PATH"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_env_denylist_multiple() { let r = flags_from_vec(svec!["deno", "run", "--deny-env=HOME,PATH", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { deny_env: Some(svec!["HOME", "PATH"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_env_ignorelist_multiple() { let r = flags_from_vec(svec!["deno", "run", "--ignore-env=HOME,PATH", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { ignore_env: Some(svec!["HOME", "PATH"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_env_allowlist_validator() { let r = flags_from_vec(svec!["deno", "run", "--allow-env=HOME", "script.ts"]); assert!(r.is_ok()); let r = flags_from_vec(svec!["deno", "--allow-env=H=ME", "script.ts"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "run", "--allow-env=H\0ME", "script.ts"]); assert!(r.is_err()); } #[test] fn deny_env_denylist_validator() { let r = flags_from_vec(svec!["deno", "run", "--deny-env=HOME", "script.ts"]); assert!(r.is_ok()); let r = flags_from_vec(svec!["deno", "run", "--deny-env=H=ME", "script.ts"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "--deny-env=H\0ME", "script.ts"]); assert!(r.is_err()); } #[test] fn allow_sys() { let r = flags_from_vec(svec!["deno", "run", "--allow-sys", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_sys: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_sys() { let r = flags_from_vec(svec!["deno", "run", "--deny-sys", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { deny_sys: Some(vec![]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_sys_allowlist() { let r = flags_from_vec(svec!["deno", "run", "--allow-sys=hostname", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_sys: Some(svec!["hostname"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_sys_denylist() { let r = flags_from_vec(svec!["deno", "--deny-sys=hostname", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), permissions: PermissionFlags { deny_sys: Some(svec!["hostname"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_sys_allowlist_multiple() { let r = flags_from_vec(svec![ "deno", "run", "--allow-sys=hostname,osRelease", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_sys: Some(svec!["hostname", "osRelease"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_sys_denylist_multiple() { let r = flags_from_vec(svec![ "deno", "run", "--deny-sys=hostname,osRelease", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { deny_sys: Some(svec!["hostname", "osRelease"]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_sys_allowlist_validator() { let r = flags_from_vec(svec!["deno", "run", "--allow-sys=hostname", "script.ts"]); assert!(r.is_ok()); let r = flags_from_vec(svec![ "deno", "run", "--allow-sys=hostname,osRelease", "script.ts" ]); assert!(r.is_ok()); let r = flags_from_vec(svec!["deno", "run", "--allow-sys=foo", "script.ts"]); assert!(r.is_err()); let r = flags_from_vec(svec![ "deno", "run", "--allow-sys=hostname,foo", "script.ts" ]); assert!(r.is_err()); } #[test] fn deny_sys_denylist_validator() { let r = flags_from_vec(svec!["deno", "run", "--deny-sys=hostname", "script.ts"]); assert!(r.is_ok()); let r = flags_from_vec(svec![ "deno", "run", "--deny-sys=hostname,osRelease", "script.ts" ]); assert!(r.is_ok()); let r = flags_from_vec(svec!["deno", "run", "--deny-sys=foo", "script.ts"]); assert!(r.is_err()); let r = flags_from_vec(svec![ "deno", "run", "--deny-sys=hostname,foo", "script.ts" ]); assert!(r.is_err()); } #[test] fn reload_validator() { let r = flags_from_vec(svec![ "deno", "run", "--reload=http://deno.land/", "script.ts" ]); assert!(r.is_ok(), "should accept valid urls"); let r = flags_from_vec(svec![ "deno", "run", "--reload=http://deno.land/a,http://deno.land/b", "script.ts" ]); assert!(r.is_ok(), "should accept accept multiple valid urls"); let r = flags_from_vec(svec![ "deno", "run", "--reload=./relativeurl/", "script.ts" ]); assert!(r.is_err(), "Should reject relative urls that start with ./"); let r = flags_from_vec(svec!["deno", "run", "--reload=relativeurl/", "script.ts"]); assert!(r.is_err(), "Should reject relative urls"); let r = flags_from_vec(svec!["deno", "run", "--reload=/absolute", "script.ts"]); assert!(r.is_err(), "Should reject absolute urls"); let r = flags_from_vec(svec!["deno", "--reload=/", "script.ts"]); assert!(r.is_err(), "Should reject absolute root url"); let r = flags_from_vec(svec!["deno", "run", "--reload=", "script.ts"]); assert!(r.is_err(), "Should reject when nothing is provided"); let r = flags_from_vec(svec!["deno", "run", "--reload=,", "script.ts"]); assert!(r.is_err(), "Should reject when a single comma is provided"); let r = flags_from_vec(svec![ "deno", "run", "--reload=,http://deno.land/a", "script.ts" ]); assert!(r.is_err(), "Should reject a leading comma"); let r = flags_from_vec(svec![ "deno", "run", "--reload=http://deno.land/a,", "script.ts" ]); assert!(r.is_err(), "Should reject a trailing comma"); } #[test] fn run_import_map() { let r = flags_from_vec(svec![ "deno", "run", "--import-map=import_map.json", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), import_map_path: Some("import_map.json".to_owned()), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn info_import_map() { let r = flags_from_vec(svec![ "deno", "info", "--import-map=import_map.json", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { file: Some("script.ts".to_string()), json: false, }), import_map_path: Some("import_map.json".to_owned()), ..Flags::default() } ); } #[test] fn cache_import_map() { let r = flags_from_vec(svec![ "deno", "cache", "--import-map=import_map.json", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Cache(CacheFlags { files: svec!["script.ts"], }), import_map_path: Some("import_map.json".to_owned()), ..Flags::default() } ); } #[test] fn doc_import_map() { let r = flags_from_vec(svec![ "deno", "doc", "--import-map=import_map.json", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { source_files: DocSourceFileFlag::Paths(vec!["script.ts".to_owned()]), private: false, json: false, html: None, lint: false, filter: None, }), import_map_path: Some("import_map.json".to_owned()), ..Flags::default() } ); } #[test] fn run_env_default() { let r = flags_from_vec(svec!["deno", "run", "--env", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), env_file: Some(vec![".env".to_owned()]), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_env_file_default() { let r = flags_from_vec(svec!["deno", "run", "--env-file", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), env_file: Some(vec![".env".to_owned()]), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_no_code_cache() { let r = flags_from_vec(svec!["deno", "--no-code-cache", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), ..Flags::default() } ); } #[test] fn run_env_defined() { let r = flags_from_vec(svec!["deno", "run", "--env=.another_env", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), env_file: Some(vec![".another_env".to_owned()]), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_env_file_defined() { let r = flags_from_vec(svec![ "deno", "run", "--env-file=.another_env", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), env_file: Some(vec![".another_env".to_owned()]), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_multiple_env_file_defined() { let r = flags_from_vec(svec![ "deno", "run", "--env-file", "--env-file=.two_env", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), env_file: Some(vec![".env".to_owned(), ".two_env".to_owned()]), code_cache_enabled: true, ..Flags::default() } ); } // The dependency and registry subcommands also accept --env-file so that // registry credentials kept in a .env file can be loaded before the command // resolves a dependency graph or talks to a registry. #[test] fn dep_and_registry_subcommands_env_file() { // (args, expected env_file) pairs, one per supported subcommand. let cases: Vec<(Vec<String>, Vec<String>)> = vec![ ( svec!["deno", "add", "--env-file", "@david/which"], svec![".env"], ), (svec!["deno", "audit", "--env-file"], svec![".env"]), (svec!["deno", "why", "--env-file", "express"], svec![".env"]), (svec!["deno", "outdated", "--env-file"], svec![".env"]), (svec!["deno", "update", "--env-file"], svec![".env"]), ( svec!["deno", "bundle", "--env-file", "main.ts"], svec![".env"], ), ( svec!["deno", "check", "--env-file", "main.ts"], svec![".env"], ), (svec!["deno", "doc", "--env-file", "main.ts"], svec![".env"]), ( svec!["deno", "info", "--env-file", "main.ts"], svec![".env"], ), (svec!["deno", "ci", "--env-file"], svec![".env"]), (svec!["deno", "publish", "--env-file"], svec![".env"]), (svec!["deno", "pack", "--env-file"], svec![".env"]), ]; for (args, expected) in cases { let flags = flags_from_vec(args.clone()) .unwrap_or_else(|e| panic!("failed to parse {args:?}: {e}")); assert_eq!( flags.env_file, Some(expected), "unexpected env_file for {args:?}", ); } } #[test] fn dep_and_registry_subcommands_env_file_explicit_and_multiple() { // An explicit path is honored. let flags = flags_from_vec(svec!["deno", "check", "--env-file=.prod.env", "main.ts"]) .unwrap(); assert_eq!(flags.env_file, Some(svec![".prod.env"])); // The --env alias works the same as --env-file. let flags = flags_from_vec(svec!["deno", "outdated", "--env=.prod.env"]).unwrap(); assert_eq!(flags.env_file, Some(svec![".prod.env"])); // Multiple --env-file flags accumulate in order. let flags = flags_from_vec(svec![ "deno", "publish", "--env-file", "--env-file=.prod.env" ]) .unwrap(); assert_eq!( flags.env_file, Some(svec![".env".to_owned(), ".prod.env".to_owned()]) ); } #[test] fn cache_multiple() { let r = flags_from_vec(svec!["deno", "cache", "script.ts", "script_two.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Cache(CacheFlags { files: svec!["script.ts", "script_two.ts"], }), ..Flags::default() } ); } #[test] fn run_seed() { let r = flags_from_vec(svec!["deno", "run", "--seed", "250", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), seed: Some(250_u64), v8_flags: svec!["--random-seed=250"], code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_seed_with_v8_flags() { let r = flags_from_vec(svec![ "deno", "run", "--seed", "250", "--v8-flags=--expose-gc", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), seed: Some(250_u64), v8_flags: svec!["--expose-gc", "--random-seed=250"], code_cache_enabled: true, ..Flags::default() } ); } #[test] fn install() { let r = flags_from_vec(svec![ "deno", "install", "-g", "jsr:@std/http/file-server", "npm:chalk", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Install(InstallFlags::Global( InstallFlagsGlobal { name: None, module_urls: svec!["jsr:@std/http/file-server", "npm:chalk"], args: vec![], root: None, force: false, compile: false, } ),), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "install", "-g", "jsr:@std/http/file-server"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Install(InstallFlags::Global( InstallFlagsGlobal { name: None, module_urls: svec!["jsr:@std/http/file-server"], args: vec![], root: None, force: false, compile: false, } ),), ..Flags::default() } ); } #[test] fn install_with_flags() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "install", "--global", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--name", "file_server", "--root", "/foo", "--force", "--env=.example.env", "jsr:@std/http/file-server", "--", "foo", "bar"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Install(InstallFlags::Global( InstallFlagsGlobal { name: Some("file_server".to_string()), module_urls: svec!["jsr:@std/http/file-server"], args: svec!["foo", "bar"], root: Some("/foo".to_string()), force: true, compile: false, } ),), import_map_path: Some("import_map.json".to_string()), no_remote: true, config_flag: ConfigFlag::Path("tsconfig.json".to_owned()), type_check_mode: TypeCheckMode::None, reload: true, lock: Some(String::from("lock.json")), ca_data: Some(CaData::File("example.crt".to_string())), cached_only: true, v8_flags: svec!["--help", "--random-seed=1"], seed: Some(1), inspect: Some("127.0.0.1:9229".parse().unwrap()), unsafely_ignore_certificate_errors: Some(vec![]), permissions: PermissionFlags { allow_net: Some(vec![]), allow_read: Some(vec![]), ..Default::default() }, env_file: Some(vec![".example.env".to_owned()]), ..Flags::default() } ); } #[test] fn uninstall() { let r = flags_from_vec(svec!["deno", "uninstall"]); assert!(r.is_err(),); let r = flags_from_vec(svec![ "deno", "uninstall", "--frozen", "--lockfile-only", "@std/load" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Local(RemoveFlags { packages: vec!["@std/load".to_string()], lockfile_only: true, package_json: false, }), }), frozen_lockfile: Some(true), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "uninstall", "file_server", "@std/load"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Local(RemoveFlags { packages: vec!["file_server".to_string(), "@std/load".to_string()], lockfile_only: false, package_json: false, }), }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "uninstall", "-g", "file_server"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Global(UninstallFlagsGlobal { packages: svec!["file_server"], root: None, }), }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "uninstall", "-g", "--root", "/user/foo/bar", "file_server" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Global(UninstallFlagsGlobal { packages: svec!["file_server"], root: Some("/user/foo/bar".to_string()), }), }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "uninstall", "-g", "--root", "/user/foo/bar", "cowsay", "file_server" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Global(UninstallFlagsGlobal { packages: svec!["cowsay", "file_server"], root: Some("/user/foo/bar".to_string()), }), }), ..Flags::default() } ); } #[test] fn uninstall_with_help_flag() { let r = flags_from_vec(svec!["deno", "uninstall", "--help"]); assert!(r.is_ok()); } #[test] fn log_level() { let r = flags_from_vec(svec!["deno", "run", "--log-level=debug", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), log_level: Some(Level::Debug), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn quiet() { let r = flags_from_vec(svec!["deno", "-q", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), log_level: Some(Level::Error), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn completions() { let r = flags_from_vec(svec!["deno", "completions", "zsh"]).unwrap(); match r.subcommand { DenoSubcommand::Completions(CompletionsFlags::Static(buf)) => { assert!(!buf.is_empty()) } _ => unreachable!(), } } #[test] fn run_with_args() { let r = flags_from_vec(svec![ "deno", "run", "script.ts", "--allow-read", "--allow-net" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), argv: svec!["--allow-read", "--allow-net"], code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--location", "https:foo", "--allow-read", "script.ts", "--allow-net", "-r", "--help", "--foo", "bar" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), location: Some(Url::parse("https://foo/").unwrap()), permissions: PermissionFlags { allow_read: Some(vec![]), ..Default::default() }, argv: svec!["--allow-net", "-r", "--help", "--foo", "bar"], code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "script.ts", "foo", "bar"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), argv: svec!["foo", "bar"], code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "script.ts", "-"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), argv: svec!["-"], code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "script.ts", "-", "foo", "bar"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), argv: svec!["-", "foo", "bar"], code_cache_enabled: true, ..Flags::default() } ); } #[test] fn no_check() { let r = flags_from_vec(svec!["deno", "--no-check", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), type_check_mode: TypeCheckMode::None, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn no_check_remote() { let r = flags_from_vec(svec!["deno", "run", "--no-check=remote", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn repl_with_unsafely_ignore_certificate_errors() { let r = flags_from_vec(svec![ "deno", "repl", "--eval", "console.log('hello');", "--unsafely-ignore-certificate-errors" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: Some("console.log('hello');".to_string()), is_default_command: false, json: false, }), unsafely_ignore_certificate_errors: Some(vec![]), type_check_mode: TypeCheckMode::None, ..Flags::default() } ); } #[test] fn run_with_unsafely_ignore_certificate_errors() { let r = flags_from_vec(svec![ "deno", "run", "--unsafely-ignore-certificate-errors", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), unsafely_ignore_certificate_errors: Some(vec![]), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_with_unsafely_treat_insecure_origin_as_secure_with_ipv6_address() { let r = flags_from_vec(svec![ "deno", "run", "--unsafely-ignore-certificate-errors=deno.land,localhost,[::],127.0.0.1,[::1],1.2.3.4", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), unsafely_ignore_certificate_errors: Some(svec![ "deno.land", "localhost", "[::]", "127.0.0.1", "[::1]", "1.2.3.4" ]), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn repl_with_unsafely_treat_insecure_origin_as_secure_with_ipv6_address() { let r = flags_from_vec(svec![ "deno", "repl", "--unsafely-ignore-certificate-errors=deno.land,localhost,[::],127.0.0.1,[::1],1.2.3.4" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None, is_default_command: false, json: false, }), unsafely_ignore_certificate_errors: Some(svec![ "deno.land", "localhost", "[::]", "127.0.0.1", "[::1]", "1.2.3.4" ]), type_check_mode: TypeCheckMode::None, ..Flags::default() } ); } #[test] fn no_remote() { let r = flags_from_vec(svec!["deno", "run", "--no-remote", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), no_remote: true, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn no_npm() { let r = flags_from_vec(svec!["deno", "run", "--no-npm", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), no_npm: true, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn local_npm() { let r = flags_from_vec(svec!["deno", "--node-modules-dir", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), node_modules_dir: Some(NodeModulesDirMode::Auto), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn vendor_flag() { let r = flags_from_vec(svec!["deno", "run", "--vendor", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), vendor: Some(true), code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "--vendor=false", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), vendor: Some(false), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn cached_only() { let r = flags_from_vec(svec!["deno", "run", "--cached-only", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), cached_only: true, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_net_allowlist_with_ports() { let r = flags_from_vec(svec![ "deno", "run", "--allow-net=deno.land,:8000,:4545", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_net: Some(svec![ "deno.land", "0.0.0.0:8000", "127.0.0.1:8000", "localhost:8000", "0.0.0.0:4545", "127.0.0.1:4545", "localhost:4545" ]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_net_denylist_with_ports() { let r = flags_from_vec(svec![ "deno", "run", "--deny-net=deno.land,:8000,:4545", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { deny_net: Some(svec![ "deno.land", "0.0.0.0:8000", "127.0.0.1:8000", "localhost:8000", "0.0.0.0:4545", "127.0.0.1:4545", "localhost:4545" ]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn allow_net_allowlist_with_ipv6_address() { let r = flags_from_vec(svec![ "deno", "run", "--allow-net=deno.land,deno.land:80,[::],127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { allow_net: Some(svec![ "deno.land", "deno.land:80", "[::]", "127.0.0.1", "[::1]", "1.2.3.4:5678", "0.0.0.0:5678", "127.0.0.1:5678", "localhost:5678", "[::1]:8080" ]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn deny_net_denylist_with_ipv6_address() { let r = flags_from_vec(svec![ "deno", "run", "--deny-net=deno.land,deno.land:80,[::],127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), permissions: PermissionFlags { deny_net: Some(svec![ "deno.land", "deno.land:80", "[::]", "127.0.0.1", "[::1]", "1.2.3.4:5678", "0.0.0.0:5678", "127.0.0.1:5678", "localhost:5678", "[::1]:8080" ]), ..Default::default() }, code_cache_enabled: true, ..Flags::default() } ); } // PORT-SKIP: no_colon_in_value_name walks the whole CommandDef tree; kept in cli/args/flags.rs #[test] fn test_with_flags() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "test", "--no-npm", "--no-remote", "--trace-leaks", "--no-run", "--filter", "- foo", "--coverage=cov", "--clean", "--location", "https:foo", "--allow-net", "--permit-no-files", "dir1/", "dir2/", "--", "arg1", "arg2"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: true, doc: false, fail_fast: None, filter: Some("- foo".to_string()), permit_no_files: true, files: FileFlags { include: vec!["dir1/".to_string(), "dir2/".to_string()], ignore: vec![], }, shuffle: None, retry: 0, repeats: 0, shard: None, parallel: false, trace_leaks: true, sanitize_ops: false, sanitize_resources: false, coverage_dir: Some("cov".to_string()), coverage_raw_data_only: false, coverage_threshold: None, clean: true, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), no_npm: true, no_remote: true, location: Some(Url::parse("https://foo/").unwrap()), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, allow_net: Some(vec![]), ..Default::default() }, argv: svec!["arg1", "arg2"], ..Flags::default() } ); } #[test] fn run_with_cafile() { let r = flags_from_vec(svec!["deno", "run", "--cert", "example.crt", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), ca_data: Some(CaData::File("example.crt".to_owned())), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_with_base64_cafile() { let r = flags_from_vec(svec![ "deno", "run", "--cert", "base64:bWVvdw==", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), ca_data: Some(CaData::Bytes(b"meow".into())), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn run_with_enable_testing_features() { let r = flags_from_vec(svec![ "deno", "run", "--enable-testing-features-do-not-use", "script.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), enable_testing_features: true, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn test_with_fail_fast() { let r = flags_from_vec(svec!["deno", "test", "--fail-fast=3"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: false, doc: false, fail_fast: Some(NonZeroUsize::new(3).unwrap()), filter: None, permit_no_files: false, shuffle: None, retry: 0, repeats: 0, shard: None, files: FileFlags { include: vec![], ignore: vec![], }, parallel: false, trace_leaks: false, sanitize_ops: false, sanitize_resources: false, coverage_dir: None, coverage_raw_data_only: false, coverage_threshold: None, clean: false, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "test", "--fail-fast=0"]); assert!(r.is_err()); } #[test] fn test_with_enable_testing_features() { let r = flags_from_vec(svec![ "deno", "test", "--enable-testing-features-do-not-use" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: false, doc: false, fail_fast: None, filter: None, permit_no_files: false, shuffle: None, retry: 0, repeats: 0, shard: None, files: FileFlags { include: vec![], ignore: vec![], }, parallel: false, trace_leaks: false, sanitize_ops: false, sanitize_resources: false, coverage_dir: None, coverage_raw_data_only: false, coverage_threshold: None, clean: false, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, enable_testing_features: true, ..Flags::default() } ); } #[test] fn test_changed() { let r = flags_from_vec(svec!["deno", "test", "--changed"]); assert_eq!( r.unwrap().subcommand, DenoSubcommand::Test(TestFlags { changed: Some(None), ..Default::default() }) ); let r = flags_from_vec(svec!["deno", "test", "--changed=origin/main"]); assert_eq!( r.unwrap().subcommand, DenoSubcommand::Test(TestFlags { changed: Some(Some("origin/main".to_string())), ..Default::default() }) ); // space-separated value is not allowed (would be ambiguous with file args) let r = flags_from_vec(svec!["deno", "test", "--changed", "HEAD~1"]); assert_eq!( r.unwrap().subcommand, DenoSubcommand::Test(TestFlags { changed: Some(None), files: FileFlags { include: vec!["HEAD~1".to_string()], ignore: vec![], }, ..Default::default() }) ); } #[test] fn test_related() { let r = flags_from_vec(svec!["deno", "test", "--related=src/a.ts,src/b.ts"]); assert_eq!( r.unwrap().subcommand, DenoSubcommand::Test(TestFlags { related: svec!["src/a.ts", "src/b.ts"], ..Default::default() }) ); } #[test] fn test_changed_conflicts_with_watch() { let r = flags_from_vec(svec!["deno", "test", "--changed", "--watch"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "test", "--related=a.ts", "--watch"]); assert!(r.is_err()); } #[test] fn test_reporter() { let r = flags_from_vec(svec!["deno", "test", "--reporter=pretty"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { reporter: TestReporterConfig::Pretty, ..Default::default() }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "test", "--reporter=dot"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { reporter: TestReporterConfig::Dot, ..Default::default() }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, log_level: Some(Level::Error), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "test", "--reporter=junit"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { reporter: TestReporterConfig::Junit, ..Default::default() }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "test", "--reporter=tap"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { reporter: TestReporterConfig::Tap, ..Default::default() }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, log_level: Some(Level::Error), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "test", "--reporter=dot", "--junit-path=report.xml" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { reporter: TestReporterConfig::Dot, junit_path: Some("report.xml".to_string()), ..Default::default() }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, log_level: Some(Level::Error), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "test", "--junit-path"]); assert!(r.is_err()); } #[test] fn test_shuffle() { let r = flags_from_vec(svec!["deno", "test", "--shuffle=1"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: false, doc: false, fail_fast: None, filter: None, permit_no_files: false, shuffle: Some(1), retry: 0, repeats: 0, shard: None, files: FileFlags { include: vec![], ignore: vec![], }, parallel: false, trace_leaks: false, sanitize_ops: false, sanitize_resources: false, coverage_dir: None, coverage_raw_data_only: false, coverage_threshold: None, clean: false, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, ..Flags::default() } ); } #[test] fn test_retry_and_repeats() { let r = flags_from_vec(svec!["deno", "test", "--retry=3", "--repeats=2"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: false, doc: false, fail_fast: None, filter: None, permit_no_files: false, shuffle: None, retry: 3, repeats: 2, shard: None, files: FileFlags { include: vec![], ignore: vec![], }, parallel: false, trace_leaks: false, sanitize_ops: false, sanitize_resources: false, coverage_dir: None, coverage_raw_data_only: false, coverage_threshold: None, clean: false, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, ..Flags::default() } ); } #[test] fn test_shard() { let r = flags_from_vec(svec!["deno", "test", "--shard=2/3"]); let flags = r.unwrap(); assert!(matches!( flags.subcommand, DenoSubcommand::Test(TestFlags { shard: Some((2, 3)), .. }) )); // Invalid shard values are rejected at parse time. assert!(flags_from_vec(svec!["deno", "test", "--shard=3/2"]).is_err()); assert!(flags_from_vec(svec!["deno", "test", "--shard=0/2"]).is_err()); assert!(flags_from_vec(svec!["deno", "test", "--shard=1/0"]).is_err()); assert!(flags_from_vec(svec!["deno", "test", "--shard=foo"]).is_err()); } #[test] fn test_watch() { let r = flags_from_vec(svec!["deno", "test", "--watch"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: false, doc: false, fail_fast: None, filter: None, permit_no_files: false, shuffle: None, retry: 0, repeats: 0, shard: None, files: FileFlags { include: vec![], ignore: vec![], }, parallel: false, trace_leaks: false, sanitize_ops: false, sanitize_resources: false, coverage_dir: None, coverage_raw_data_only: false, coverage_threshold: None, clean: false, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, watch: Some(Default::default()), ..Flags::default() } ); } #[test] fn test_watch_explicit_cwd() { let r = flags_from_vec(svec!["deno", "test", "--watch", "./"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: false, doc: false, fail_fast: None, filter: None, permit_no_files: false, shuffle: None, retry: 0, repeats: 0, shard: None, files: FileFlags { include: vec!["./".to_string()], ignore: vec![], }, parallel: false, trace_leaks: false, sanitize_ops: false, sanitize_resources: false, coverage_dir: None, coverage_raw_data_only: false, coverage_threshold: None, clean: false, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, watch: Some(Default::default()), ..Flags::default() } ); } #[test] fn test_watch_with_no_clear_screen() { let r = flags_from_vec(svec!["deno", "test", "--watch", "--no-clear-screen"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { no_run: false, doc: false, fail_fast: None, filter: None, permit_no_files: false, shuffle: None, retry: 0, repeats: 0, shard: None, files: FileFlags { include: vec![], ignore: vec![], }, parallel: false, trace_leaks: false, sanitize_ops: false, sanitize_resources: false, coverage_dir: None, coverage_raw_data_only: false, coverage_threshold: None, clean: false, reporter: Default::default(), junit_path: None, hide_stacktraces: false, changed: None, related: vec![], update_snapshots: false, }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, watch: Some(WatchFlagsWithPaths { hmr: false, no_clear_screen: true, exclude: vec![], paths: vec![], }), ..Flags::default() } ); } #[test] fn test_watch_with_paths() { let r = flags_from_vec(svec!("deno", "test", "--watch=foo")); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Test(TestFlags { ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("foo")], no_clear_screen: false, exclude: vec![], }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "test", "--watch=foo,bar"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Test(TestFlags { ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("foo"), String::from("bar")], no_clear_screen: false, exclude: vec![], }), ..Flags::default() } ); } #[test] fn test_watch_with_excluded_paths() { let r = flags_from_vec(svec!("deno", "test", "--watch", "--watch-exclude=foo",)); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Test(TestFlags { ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![], no_clear_screen: false, exclude: vec![String::from("foo")], }), ..Flags::default() } ); let r = flags_from_vec( svec!("deno", "test", "--watch=foo", "--watch-exclude=bar",), ); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Test(TestFlags { ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("foo")], no_clear_screen: false, exclude: vec![String::from("bar")], }), ..Flags::default() } ); let r = flags_from_vec( svec!["deno", "test", "--watch", "--watch-exclude=foo,bar",], ); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Test(TestFlags { ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![], no_clear_screen: false, exclude: vec![String::from("foo"), String::from("bar")], }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "test", "--watch=foo,bar", "--watch-exclude=baz,qux", ]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Test(TestFlags { ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("foo"), String::from("bar")], no_clear_screen: false, exclude: vec![String::from("baz"), String::from("qux"),], }), ..Flags::default() } ); } #[test] fn test_coverage_default_dir() { let r = flags_from_vec(svec!["deno", "test", "--coverage"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { coverage_dir: Some("coverage".to_string()), ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn test_hide_stacktraces() { let r = flags_from_vec(svec!["deno", "test", "--hide-stacktraces"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Test(TestFlags { hide_stacktraces: true, update_snapshots: false, ..TestFlags::default() }), type_check_mode: TypeCheckMode::Local, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn upgrade_with_ca_file() { let r = flags_from_vec(svec!["deno", "upgrade", "--cert", "example.crt"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: false, dry_run: false, canary: false, no_delta: false, release_candidate: false, version: None, output: None, version_or_hash_or_channel: None, checksum: None, pr: None, branch: None, }), ca_data: Some(CaData::File("example.crt".to_owned())), ..Flags::default() } ); } #[test] fn upgrade_release_candidate() { let r = flags_from_vec(svec!["deno", "upgrade", "--rc"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: false, dry_run: false, canary: false, no_delta: false, release_candidate: true, version: None, output: None, version_or_hash_or_channel: None, checksum: None, pr: None, branch: None, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "upgrade", "--rc", "--canary"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "upgrade", "--rc", "--version"]); assert!(r.is_err()); } #[test] fn upgrade_pr() { let r = flags_from_vec(svec!["deno", "upgrade", "pr", "12345"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: false, dry_run: false, canary: false, no_delta: false, release_candidate: false, version: None, output: None, version_or_hash_or_channel: None, checksum: None, pr: Some(12345), branch: None, }), ..Flags::default() } ); } #[test] fn upgrade_pr_with_hash_prefix() { let r = flags_from_vec(svec!["deno", "upgrade", "pr", "#6789"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: false, dry_run: false, canary: false, no_delta: false, release_candidate: false, version: None, output: None, version_or_hash_or_channel: None, checksum: None, pr: Some(6789), branch: None, }), ..Flags::default() } ); } #[test] fn upgrade_pr_with_flags() { let r = flags_from_vec(svec!["deno", "upgrade", "--dry-run", "pr", "33250"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: false, dry_run: true, canary: false, no_delta: false, release_candidate: false, version: None, output: None, version_or_hash_or_channel: None, checksum: None, pr: Some(33250), branch: None, }), ..Flags::default() } ); } #[test] fn upgrade_pr_missing_number() { let r = flags_from_vec(svec!["deno", "upgrade", "pr"]); assert!(r.is_err()); } #[test] fn upgrade_pr_invalid_number() { let r = flags_from_vec(svec!["deno", "upgrade", "pr", "abc"]); assert!(r.is_err()); } #[test] fn cache_with_cafile() { let r = flags_from_vec(svec![ "deno", "cache", "--cert", "example.crt", "script.ts", "script_two.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Cache(CacheFlags { files: svec!["script.ts", "script_two.ts"], }), ca_data: Some(CaData::File("example.crt".to_owned())), ..Flags::default() } ); } #[test] fn info_with_cafile() { let r = flags_from_vec(svec![ "deno", "info", "--cert", "example.crt", "https://example.com" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Info(InfoFlags { json: false, file: Some("https://example.com".to_string()), }), ca_data: Some(CaData::File("example.crt".to_owned())), ..Flags::default() } ); } #[test] fn doc() { let r = flags_from_vec(svec!["deno", "doc", "--json", "path/to/module.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, json: true, html: None, lint: false, source_files: DocSourceFileFlag::Paths(svec!["path/to/module.ts"]), filter: None, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "doc", "--html", "path/to/module.ts"]); assert!(r.is_ok()); let r = flags_from_vec(svec![ "deno", "doc", "--html", "--name=My library", "path/to/module.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, json: false, lint: false, html: Some(DocHtmlFlag { name: Some("My library".to_string()), category_docs_path: None, symbol_redirect_map_path: None, default_symbol_map_path: None, strip_trailing_html: false, output: String::from("./docs/"), }), source_files: DocSourceFileFlag::Paths(svec!["path/to/module.ts"]), filter: None, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "doc", "--html", "--name=My library", "--lint", "--output=./foo", "path/to/module.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, json: false, html: Some(DocHtmlFlag { name: Some("My library".to_string()), category_docs_path: None, symbol_redirect_map_path: None, default_symbol_map_path: None, strip_trailing_html: false, output: String::from("./foo"), }), lint: true, source_files: DocSourceFileFlag::Paths(svec!["path/to/module.ts"]), filter: None, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "doc", "--html", "--name=My library",]); assert!(r.is_err()); let r = flags_from_vec(svec![ "deno", "doc", "--filter", "SomeClass.someField", "path/to/module.ts", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, json: false, html: None, lint: false, source_files: DocSourceFileFlag::Paths(vec![ "path/to/module.ts".to_string() ]), filter: Some("SomeClass.someField".to_string()), }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "doc"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, json: false, html: None, lint: false, source_files: Default::default(), filter: None, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "doc", "--filter", "Deno.Listener", "--builtin" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, lint: false, json: false, html: None, source_files: DocSourceFileFlag::Builtin, filter: Some("Deno.Listener".to_string()), }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "doc", "--no-npm", "--no-remote", "--private", "path/to/module.js" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: true, lint: false, json: false, html: None, source_files: DocSourceFileFlag::Paths(svec!["path/to/module.js"]), filter: None, }), no_npm: true, no_remote: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "doc", "path/to/module.js", "path/to/module2.js" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, lint: false, json: false, html: None, source_files: DocSourceFileFlag::Paths(vec![ "path/to/module.js".to_string(), "path/to/module2.js".to_string() ]), filter: None, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "doc", "path/to/module.js", "--builtin", "path/to/module2.js" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, json: false, html: None, lint: false, source_files: DocSourceFileFlag::Paths(vec![ "path/to/module.js".to_string(), "path/to/module2.js".to_string() ]), filter: None, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "doc", "--lint",]); assert!(r.is_err()); let r = flags_from_vec(svec![ "deno", "doc", "--lint", "path/to/module.js", "path/to/module2.js" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Doc(DocFlags { private: false, lint: true, json: false, html: None, source_files: DocSourceFileFlag::Paths(vec![ "path/to/module.js".to_string(), "path/to/module2.js".to_string() ]), filter: None, }), ..Flags::default() } ); } #[test] fn inspect_default_host() { let r = flags_from_vec(svec!["deno", "run", "--inspect", "foo.js"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "foo.js".to_string(), )), inspect: Some("127.0.0.1:9229".parse().unwrap()), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn inspect_wait() { let r = flags_from_vec(svec!["deno", "--inspect-wait", "foo.js"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "foo.js".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), inspect_wait: Some("127.0.0.1:9229".parse().unwrap()), code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--inspect-wait=127.0.0.1:3567", "foo.js" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "foo.js".to_string(), )), inspect_wait: Some("127.0.0.1:3567".parse().unwrap()), code_cache_enabled: true, ..Flags::default() } ); } #[test] fn compile() { let r = flags_from_vec(svec![ "deno", "compile", "https://examples.deno.land/color-logging.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Compile(CompileFlags { source_file: "https://examples.deno.land/color-logging.ts".to_string(), output: None, args: vec![], target: None, no_terminal: false, icon: None, include: Default::default(), exclude: Default::default(), eszip: false, self_extracting: false, bundle: false, app_name: None, minify: false, exclude_unused_npm: false, engine: Default::default(), }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn compile_and_desktop_engine() { let flags = flags_from_vec(svec!["deno", "compile", "--engine", "quickjs", "main.ts"]) .unwrap(); let DenoSubcommand::Compile(compile) = flags.subcommand else { panic!("expected compile subcommand"); }; assert_eq!(compile.engine, JavaScriptEngine::QuickJs); let flags = flags_from_vec(svec!["deno", "desktop", "--engine", "quickjs", "main.tsx"]) .unwrap(); let DenoSubcommand::Desktop(desktop) = flags.subcommand else { panic!("expected desktop subcommand"); }; assert_eq!(desktop.engine, JavaScriptEngine::QuickJs); } #[test] fn compile_target_aarch64_windows() { // The denort runtime for Windows arm64 is built and published by CI, so // `--target aarch64-pc-windows-msvc` must be accepted. This is the // regression guard for the clap -> deno_cli_parser cutover: the new parser // validates `--target` against its own SUPPORTED_OS list, so the triple has // to be present here too (not just in clap's list). let r = flags_from_vec(svec![ "deno", "compile", "--target=aarch64-pc-windows-msvc", "main.ts" ]); let DenoSubcommand::Compile(c) = r.unwrap().subcommand else { panic!("expected compile subcommand"); }; assert_eq!(c.target, Some("aarch64-pc-windows-msvc".to_string())); // An unsupported triple is still rejected, proving the validator is active // (and this test isn't passing just because validation was dropped). assert!( flags_from_vec(svec![ "deno", "compile", "--target=riscv64gc-unknown-linux-gnu", "main.ts" ]) .is_err() ); } #[test] fn desktop_backend_default() { let r = flags_from_vec(svec!["deno", "desktop", "main.tsx"]); let flags = r.unwrap(); let DenoSubcommand::Desktop(desktop) = flags.subcommand else { panic!("expected desktop subcommand"); }; assert_eq!(desktop.source_file, "main.tsx"); // No --backend flag leaves the field unset so `desktop.backend` in // deno.json (or the webview default) can take effect during config merge. assert_eq!(desktop.backend.as_deref(), None); } #[test] fn desktop_backend_explicit() { let r = flags_from_vec(svec!["deno", "desktop", "--backend", "cef", "main.tsx"]); let flags = r.unwrap(); let DenoSubcommand::Desktop(desktop) = flags.subcommand else { panic!("expected desktop subcommand"); }; assert_eq!(desktop.backend.as_deref(), Some("cef")); } #[test] fn desktop_exclude_unused_npm() { let r = flags_from_vec(svec!["deno", "desktop", "main.tsx"]); let DenoSubcommand::Desktop(desktop) = r.unwrap().subcommand else { panic!("expected desktop subcommand"); }; // Off by default: the full managed npm snapshot keeps // non-statically-analyzable dynamic imports working. assert!(!desktop.exclude_unused_npm); let r = flags_from_vec(svec![ "deno", "desktop", "--exclude-unused-npm", "main.tsx" ]); let DenoSubcommand::Desktop(desktop) = r.unwrap().subcommand else { panic!("expected desktop subcommand"); }; assert!(desktop.exclude_unused_npm); } #[test] fn compile_watch_with_no_clear_screen() { let r = flags_from_vec(svec![ "deno", "compile", "--watch", "--no-clear-screen", "main.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Compile(CompileFlags { source_file: "main.ts".to_string(), output: None, args: vec![], target: None, no_terminal: false, icon: None, include: Default::default(), exclude: Default::default(), eszip: false, self_extracting: false, bundle: false, app_name: None, minify: false, exclude_unused_npm: false, engine: Default::default(), }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, watch: Some(WatchFlagsWithPaths { paths: vec![], hmr: false, no_clear_screen: true, exclude: vec![], }), ..Flags::default() } ); } #[test] fn compile_with_flags() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "compile", "--include", "include.txt", "--exclude", "exclude.txt", "--import-map", "import_map.json", "--no-code-cache", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--icon", "favicon.ico", "--output", "colors", "--env=.example.env", "https://examples.deno.land/color-logging.ts", "foo", "bar", "-p", "8080"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Compile(CompileFlags { source_file: "https://examples.deno.land/color-logging.ts".to_string(), output: Some(String::from("colors")), args: svec!["foo", "bar", "-p", "8080"], target: None, no_terminal: true, icon: Some(String::from("favicon.ico")), include: vec!["include.txt".to_string()], exclude: vec!["exclude.txt".to_string()], eszip: false, self_extracting: false, bundle: false, app_name: None, minify: false, exclude_unused_npm: false, engine: Default::default(), }), import_map_path: Some("import_map.json".to_string()), no_remote: true, code_cache_enabled: false, config_flag: ConfigFlag::Path("tsconfig.json".to_owned()), type_check_mode: TypeCheckMode::None, reload: true, lock: Some(String::from("lock.json")), ca_data: Some(CaData::File("example.crt".to_string())), cached_only: true, location: Some(Url::parse("https://foo/").unwrap()), permissions: PermissionFlags { allow_read: Some(vec![]), allow_net: Some(vec![]), ..Default::default() }, unsafely_ignore_certificate_errors: Some(vec![]), v8_flags: svec!["--help", "--random-seed=1"], seed: Some(1), env_file: Some(vec![".example.env".to_owned()]), ..Flags::default() } ); } #[test] fn coverage() { let r = flags_from_vec(svec!["deno", "coverage", "foo.json"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Coverage(CoverageFlags { files: FileFlags { include: vec!["foo.json".to_string()], ignore: vec![], }, include: vec![r"^file:".to_string()], exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()], ..CoverageFlags::default() }), ..Flags::default() } ); } #[test] fn coverage_with_threshold() { let r = flags_from_vec(svec!["deno", "coverage", "--threshold=80", "foo.json"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Coverage(CoverageFlags { files: FileFlags { include: vec!["foo.json".to_string()], ignore: vec![], }, include: vec![r"^file:".to_string()], exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()], threshold: Some(80), ..CoverageFlags::default() }), ..Flags::default() } ); } #[test] fn coverage_threshold_out_of_range() { // Percentages above 100 are rejected by the value parser. let r = flags_from_vec(svec!["deno", "coverage", "--threshold=150", "foo.json"]); assert!(r.is_err()); } #[test] fn coverage_with_lcov_and_out_file() { let r = flags_from_vec(svec![ "deno", "coverage", "--lcov", "--output=foo.lcov", "foo.json" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Coverage(CoverageFlags { files: FileFlags { include: vec!["foo.json".to_string()], ignore: vec![], }, include: vec![r"^file:".to_string()], exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()], r#type: CoverageType::Lcov, threshold: None, output: Some(String::from("foo.lcov")), }), ..Flags::default() } ); } #[test] fn coverage_with_default_files() { let r = flags_from_vec(svec!["deno", "coverage",]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Coverage(CoverageFlags { files: FileFlags { include: vec!["coverage".to_string()], ignore: vec![], }, include: vec![r"^file:".to_string()], exclude: vec![r"test\.(js|mjs|ts|jsx|tsx)$".to_string()], ..CoverageFlags::default() }), ..Flags::default() } ); } #[test] fn location_with_bad_scheme() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "run", "--location", "foo:", "mod.ts"]); assert!(r.is_err()); assert!( r.unwrap_err() .to_string() .contains("Expected protocol \"http\" or \"https\"") ); } // PORT-SKIP: test_config_path_args needs CLI-side cwd resolution; kept in cli/args/flags.rs #[test] fn test_no_clear_watch_flag_without_watch_flag() { let r = flags_from_vec(svec!["deno", "run", "--no-clear-screen", "foo.js"]); assert!(r.is_err()); let error_message = r.unwrap_err().to_string(); assert!( &error_message .contains("error: the following required arguments were not provided:") ); // PORT-NOTE: clap renders the missing arg as `--watch[=<FILES>...]`; the // new parser reports the plain flag name. assert!(&error_message.contains("--watch")); } #[test] fn task_subcommand() { let r = flags_from_vec(svec!["deno", "task", "build", "hello", "world",]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), argv: svec!["hello", "world"], ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "--cwd", "foo", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: Some("foo".to_string()), task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "--filter", "*", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: Some("*".to_string()), eval: false, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "--recursive", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: true, members: false, filter: Some("*".to_string()), eval: false, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "-r", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: true, members: false, filter: Some("*".to_string()), eval: false, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "--members", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: true, filter: Some("*".to_string()), eval: false, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "--members", "-r", "build"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "task", "--members", "-f", "*", "build"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "task", "--eval", "echo 1"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("echo 1".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: true, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "task", "--eval"]); assert!(r.is_err()); } #[test] fn task_subcommand_jobs() { // `--jobs`, its `--concurrency` alias, and the `-j` short form all parse // to the same value. for args in [ svec!["deno", "task", "--jobs", "1", "build"], svec!["deno", "task", "--concurrency", "1", "build"], svec!["deno", "task", "-j", "1", "build"], ] { let r = flags_from_vec(args.clone()); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: Some(NonZeroUsize::new(1).unwrap()), if_present: false, }), ..Flags::default() }, "unexpected parse for {args:?}" ); } // Reject zero, negative, and non-numeric values. for invalid in ["0", "-1", "abc"] { let r = flags_from_vec(svec!["deno", "task", "--jobs", invalid, "build"]); assert!(r.is_err(), "expected error for value {invalid:?}"); } } #[test] fn task_subcommand_double_hyphen() { let r = flags_from_vec(svec![ "deno", "task", "-c", "deno.json", "build", "--", "hello", "world", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), argv: svec!["--", "hello", "world"], config_flag: ConfigFlag::Path("deno.json".to_owned()), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "task", "--cwd", "foo", "build", "--", "hello", "world" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: Some("foo".to_string()), task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), argv: svec!["--", "hello", "world"], ..Flags::default() } ); } #[test] fn task_subcommand_double_hyphen_only() { // edge case, but it should forward let r = flags_from_vec(svec!["deno", "task", "build", "--"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), argv: svec!["--"], ..Flags::default() } ); } #[test] fn task_following_arg() { let r = flags_from_vec(svec!["deno", "task", "build", "-1", "--test"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), argv: svec!["-1", "--test"], ..Flags::default() } ); } #[test] fn task_following_double_hyphen_arg() { let r = flags_from_vec(svec!["deno", "task", "build", "--test"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), argv: svec!["--test"], ..Flags::default() } ); } #[test] fn task_with_global_flags() { // can fail if the custom parser in task_parse() starts at the wrong index let r = flags_from_vec(svec!["deno", "--quiet", "task", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), log_level: Some(log::Level::Error), ..Flags::default() } ); } #[test] fn task_subcommand_empty() { let r = flags_from_vec(svec!["deno", "task"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: None, is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), ..Flags::default() } ); } #[test] fn task_subcommand_config() { let r = flags_from_vec(svec!["deno", "task", "--config", "deno.jsonc"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: None, is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), config_flag: ConfigFlag::Path("deno.jsonc".to_string()), ..Flags::default() } ); } #[test] fn task_subcommand_config_short() { let r = flags_from_vec(svec!["deno", "task", "-c", "deno.jsonc"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: None, is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), config_flag: ConfigFlag::Path("deno.jsonc".to_string()), ..Flags::default() } ); } #[test] fn task_subcommand_noconfig_invalid() { let r = flags_from_vec(svec!["deno", "task", "--no-config"]); assert!(r.is_err()); } #[test] fn task_subcommand_env_file() { let r = flags_from_vec(svec!["deno", "task", "--env-file", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), env_file: Some(vec![".env".to_owned()]), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "task", "--env-file=.env.dev", "--env-file=.env.local", "build" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: false, }), env_file: Some(vec![".env.dev".to_owned(), ".env.local".to_owned()]), ..Flags::default() } ); } #[test] fn task_subcommand_if_present() { let r = flags_from_vec(svec!["deno", "task", "--if-present", "build"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Task(TaskFlags { cwd: None, task: Some("build".to_string()), is_run: false, recursive: false, members: false, filter: None, eval: false, no_prefix: false, concurrency: None, if_present: true, }), ..Flags::default() } ); } #[test] fn bench_with_flags() { let r = flags_from_vec(svec![ "deno", "bench", "--json", "--no-npm", "--no-remote", "--no-run", "--filter", "- foo", "--location", "https:foo", "--allow-net", "dir1/", "dir2/", "--", "arg1", "arg2" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Bench(BenchFlags { filter: Some("- foo".to_string()), json: true, no_run: true, files: FileFlags { include: vec!["dir1/".to_string(), "dir2/".to_string()], ignore: vec![], }, permit_no_files: false, }), no_npm: true, no_remote: true, type_check_mode: TypeCheckMode::Local, location: Some(Url::parse("https://foo/").unwrap()), permissions: PermissionFlags { allow_net: Some(vec![]), no_prompt: true, ..Default::default() }, argv: svec!["arg1", "arg2"], ..Flags::default() } ); } #[test] fn bench_watch() { let r = flags_from_vec(svec!["deno", "bench", "--watch"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Bench(BenchFlags { filter: None, json: false, no_run: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: false }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, watch: Some(Default::default()), ..Flags::default() } ); } #[test] fn bench_watch_with_paths() { let r = flags_from_vec(svec!["deno", "bench", "--watch=foo,bar"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Bench(BenchFlags { ..Default::default() }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, watch: Some(WatchFlagsWithPaths { hmr: false, paths: vec![String::from("foo"), String::from("bar")], no_clear_screen: false, exclude: vec![], }), ..Flags::default() } ); } #[test] fn minimum_dependency_age_alias() { for flag in ["--minimum-dependency-age=0", "--min-dep-age=0"] { let flags = flags_from_vec(svec!["deno", "run", flag, "script.ts"]).unwrap(); assert_eq!( flags.minimum_dependency_age, Some(NewestDependencyDate::Disabled) ); } } #[test] fn bench_no_files() { let r = flags_from_vec(svec!["deno", "bench", "--permit-no-files"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Bench(BenchFlags { filter: None, json: false, no_run: false, files: FileFlags { include: vec![], ignore: vec![], }, permit_no_files: true }), permissions: PermissionFlags { no_prompt: true, ..Default::default() }, type_check_mode: TypeCheckMode::Local, ..Flags::default() } ); } #[test] fn run_with_check() { let r = flags_from_vec(svec!["deno", "run", "--check", "script.ts",]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "--check=all", "script.ts",]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), type_check_mode: TypeCheckMode::All, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "--check=foo", "script.ts",]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), type_check_mode: TypeCheckMode::None, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "run", "--no-check", "--check", "script.ts",]); assert!(r.is_err()); } #[test] fn no_config() { let r = flags_from_vec(svec!["deno", "run", "--no-config", "script.ts",]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), config_flag: ConfigFlag::Disabled, code_cache_enabled: true, ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "run", "--config", "deno.json", "--no-config", "script.ts", ]); assert!(r.is_err()); } #[test] fn init() { let r = flags_from_vec(svec!["deno", "init"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: None, package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "foo"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: None, package_args: vec![], dir: Some(String::from("foo")), lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "--quiet"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: None, package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), log_level: Some(Level::Error), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "--lib"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: None, package_args: vec![], dir: None, lib: true, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "--serve"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: None, package_args: vec![], dir: None, lib: false, serve: true, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "foo", "--lib"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: None, package_args: vec![], dir: Some(String::from("foo")), lib: true, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "--lib", "--npm", "vite"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "init", "--serve", "--npm", "vite"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "--lib"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("npm:vite".to_string()), package_args: svec!["--lib"], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "--serve"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("npm:vite".to_string()), package_args: svec!["--serve"], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "--npm", "vite", "new_dir"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("npm:vite".to_string()), package_args: svec!["new_dir"], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "init", "--npm", "--yes", "npm:vite"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("npm:vite".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: true, }), ..Flags::default() } ); // --jsr basic let r = flags_from_vec(svec!["deno", "init", "--jsr", "@denotest/create"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@denotest/create".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); // --jsr with jsr: prefix already present let r = flags_from_vec(svec!["deno", "init", "--jsr", "jsr:@fresh/init"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@fresh/init".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); // --jsr with --yes let r = flags_from_vec(svec!["deno", "init", "--jsr", "--yes", "@denotest/create"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@denotest/create".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: true, }), ..Flags::default() } ); // --jsr with extra args let r = flags_from_vec(svec![ "deno", "init", "--jsr", "@denotest/create", "my-project" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@denotest/create".to_string()), package_args: svec!["my-project"], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); // --jsr conflicts with --npm, --lib, --serve, --empty let r = flags_from_vec(svec!["deno", "init", "--jsr", "--npm", "@foo/bar"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "init", "--jsr", "--lib", "@foo/bar"]); assert!(r.is_err()); // --jsr without package name let r = flags_from_vec(svec!["deno", "init", "--jsr"]); assert!(r.is_err()); } #[test] fn create() { let r = flags_from_vec(svec!["deno", "create"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "create", "vite"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "create", "npm:vite", "my-project"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "create", "npm:vite", "--", "my-project"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("npm:vite".to_string()), package_args: svec!["my-project"], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "create", "--npm", "vite"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("npm:vite".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "create", "--npm", "vite", "my-project"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "create", "--yes", "npm:vite"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("npm:vite".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: true, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "create", "jsr:@std/http/file-server"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@std/http/file-server".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "create", "jsr:@fresh/init"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@fresh/init".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "create", "--yes", "jsr:@fresh/init"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@fresh/init".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: true, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "create", "jsr:@fresh/init", "--", "my-project" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@fresh/init".to_string()), package_args: svec!["my-project"], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); // empty jsr: prefix let r = flags_from_vec(svec!["deno", "create", "jsr:"]); assert!(r.is_err()); // --jsr flag let r = flags_from_vec(svec!["deno", "create", "--jsr", "@fresh/init"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@fresh/init".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: false, }), ..Flags::default() } ); // --jsr with --yes let r = flags_from_vec(svec!["deno", "create", "--jsr", "--yes", "@fresh/init"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Init(InitFlags { package: Some("jsr:@fresh/init".to_string()), package_args: vec![], dir: None, lib: false, serve: false, empty: false, yes: true, }), ..Flags::default() } ); // --jsr with npm: specifier is contradictory let r = flags_from_vec(svec!["deno", "create", "--jsr", "npm:vite"]); assert!(r.is_err()); // --jsr and --npm conflict let r = flags_from_vec(svec!["deno", "create", "--jsr", "--npm", "@foo"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "create", "npm:"]); assert!(r.is_err()); // --npm with jsr: is contradictory let r = flags_from_vec(svec!["deno", "create", "--npm", "jsr:@std/http"]); assert!(r.is_err()); } #[test] fn jupyter() { let r = flags_from_vec(svec!["deno", "jupyter"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Jupyter(JupyterFlags { install: false, kernel: false, conn_file: None, name: None, display: None, force: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "jupyter", "--install"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Jupyter(JupyterFlags { install: true, kernel: false, conn_file: None, name: None, display: None, force: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--force"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Jupyter(JupyterFlags { install: true, kernel: false, conn_file: None, name: None, display: None, force: true, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "jupyter", "--install", "--name", "debugdeno", "--display", "Deno (debug)" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Jupyter(JupyterFlags { install: true, kernel: false, conn_file: None, name: Some("debugdeno".to_string()), display: Some("Deno (debug)".to_string()), force: false, }), ..Flags::default() } ); let r = flags_from_vec(svec!["deno", "jupyter", "-n", "debugdeno",]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Jupyter(JupyterFlags { install: false, kernel: false, conn_file: None, name: Some("debugdeno".to_string()), display: None, force: false, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "jupyter", "--kernel", "--conn", "path/to/conn/file" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Jupyter(JupyterFlags { install: false, kernel: true, conn_file: Some(String::from("path/to/conn/file")), name: None, display: None, force: false, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "jupyter", "--install", "--conn", "path/to/conn/file" ]); r.unwrap_err(); let r = flags_from_vec(svec!["deno", "jupyter", "--kernel",]); r.unwrap_err(); let r = flags_from_vec(svec!["deno", "jupyter", "--install", "--kernel",]); r.unwrap_err(); let r = flags_from_vec(svec!["deno", "jupyter", "--display", "deno"]); r.unwrap_err(); let r = flags_from_vec(svec!["deno", "jupyter", "--kernel", "--display"]); r.unwrap_err(); let r = flags_from_vec(svec!["deno", "jupyter", "--force"]); r.unwrap_err(); } #[test] fn publish_args() { let r = flags_from_vec(svec![ "deno", "publish", "--no-provenance", "--dry-run", "--allow-slow-types", "--allow-dirty", "--token=asdf", "--set-version=1.0.1", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Publish(PublishFlags { token: Some("asdf".to_string()), dry_run: true, allow_slow_types: true, allow_dirty: true, no_provenance: true, set_version: Some("1.0.1".to_string()), }), type_check_mode: TypeCheckMode::Local, ..Flags::default() } ); } #[test] fn add_or_install_subcommand() { let r = flags_from_vec(svec!["deno", "add"]); r.unwrap_err(); for cmd in ["add", "install"] { let mk_flags = |flags: AddFlags| -> Flags { match cmd { "add" => Flags { subcommand: DenoSubcommand::Add(flags), ..Flags::default() }, "install" => Flags { subcommand: DenoSubcommand::Install(InstallFlags::Local( InstallFlagsLocal::Add(flags), Default::default(), )), ..Flags::default() }, _ => unreachable!(), } }; { let r = flags_from_vec(svec!["deno", cmd, "@david/which"]); assert_eq!( r.unwrap(), mk_flags(AddFlags { packages: svec!["@david/which"], dev: false, // default is false optional: false, no_save: false, default_registry: Some(DefaultRegistry::Npm), lockfile_only: false, save_exact: false, package_json: false, unscoped: false, }) ); } { let r = flags_from_vec(svec![ "deno", cmd, "--frozen", "--lockfile-only", "@david/which", "@luca/hello" ]); let mut expected_flags = mk_flags(AddFlags { packages: svec!["@david/which", "@luca/hello"], dev: false, optional: false, no_save: false, default_registry: Some(DefaultRegistry::Npm), lockfile_only: true, save_exact: false, package_json: false, unscoped: false, }); expected_flags.frozen_lockfile = Some(true); assert_eq!(r.unwrap(), expected_flags); } { let r = flags_from_vec(svec!["deno", cmd, "--dev", "npm:chalk"]); assert_eq!( r.unwrap(), mk_flags(AddFlags { packages: svec!["npm:chalk"], dev: true, optional: false, no_save: false, default_registry: Some(DefaultRegistry::Npm), lockfile_only: false, save_exact: false, package_json: false, unscoped: false, }), ); } { let r = flags_from_vec(svec!["deno", cmd, "--npm", "chalk"]); assert_eq!( r.unwrap(), mk_flags(AddFlags { packages: svec!["chalk"], dev: false, optional: false, no_save: false, default_registry: Some(DefaultRegistry::Npm), lockfile_only: false, save_exact: false, package_json: false, unscoped: false, }), ); } { let r = flags_from_vec(svec!["deno", cmd, "--jsr", "@std/fs"]); assert_eq!( r.unwrap(), mk_flags(AddFlags { packages: svec!["@std/fs"], dev: false, optional: false, no_save: false, default_registry: Some(DefaultRegistry::Jsr), lockfile_only: false, save_exact: false, package_json: false, unscoped: false, }), ); } for arg in ["--save-optional", "-O"] { let r = flags_from_vec(svec!["deno", cmd, arg, "npm:chalk"]); assert_eq!( r.unwrap(), mk_flags(AddFlags { packages: svec!["npm:chalk"], dev: false, optional: true, no_save: false, default_registry: Some(DefaultRegistry::Npm), lockfile_only: false, save_exact: false, package_json: false, unscoped: false, }), ); } { let r = flags_from_vec(svec!["deno", cmd, "--no-save", "npm:chalk"]); assert_eq!( r.unwrap(), mk_flags(AddFlags { packages: svec!["npm:chalk"], dev: false, optional: false, no_save: true, default_registry: Some(DefaultRegistry::Npm), lockfile_only: false, save_exact: false, package_json: false, unscoped: false, }), ); } { let r = flags_from_vec(svec!["deno", cmd, "--unscoped", "jsr:@david/which"]); assert_eq!( r.unwrap(), mk_flags(AddFlags { packages: svec!["jsr:@david/which"], dev: false, optional: false, no_save: false, default_registry: Some(DefaultRegistry::Npm), lockfile_only: false, save_exact: false, package_json: false, unscoped: true, }), ); } // --save-optional conflicts with --dev assert!( flags_from_vec(svec![ "deno", cmd, "--save-optional", "--dev", "npm:chalk" ]) .is_err() ); // --no-save conflicts with --dev and --save-optional assert!( flags_from_vec(svec!["deno", cmd, "--no-save", "--dev", "npm:chalk"]) .is_err() ); assert!( flags_from_vec(svec![ "deno", cmd, "--no-save", "--save-optional", "npm:chalk" ]) .is_err() ); } { let r = flags_from_vec(svec![ "deno", "add", "--allow-import=example.com", "@david/which" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Add(AddFlags { packages: svec!["@david/which"], dev: false, optional: false, no_save: false, default_registry: Some(DefaultRegistry::Npm), lockfile_only: false, save_exact: false, package_json: false, unscoped: false, }), permissions: PermissionFlags { allow_import: Some(svec!["example.com"]), ..Default::default() }, ..Flags::default() } ); } } #[test] fn remove_subcommand() { let r = flags_from_vec(svec!["deno", "remove"]); r.unwrap_err(); let r = flags_from_vec(svec!["deno", "remove", "@david/which"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Remove(RemoveFlags { packages: svec!["@david/which"], lockfile_only: false, package_json: false, }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "remove", "--frozen", "--lockfile-only", "@david/which", "@luca/hello" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Remove(RemoveFlags { packages: svec!["@david/which", "@luca/hello"], lockfile_only: true, package_json: false, }), frozen_lockfile: Some(true), ..Flags::default() } ); } #[test] fn remove_global_alias_for_uninstall() { // `deno remove --global <name>` is an alias for `deno uninstall --global // <name>` and produces the exact same subcommand. for global_flag in ["--global", "-g"] { let r = flags_from_vec(svec!["deno", "remove", global_flag, "file_server"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Global(UninstallFlagsGlobal { packages: svec!["file_server"], root: None, }), }), ..Flags::default() } ); } // `--root` is honored, just like `deno uninstall --global --root`. let r = flags_from_vec(svec![ "deno", "remove", "-g", "--root", "/user/foo/bar", "file_server" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Global(UninstallFlagsGlobal { packages: svec!["file_server"], root: Some("/user/foo/bar".to_string()), }), }), ..Flags::default() } ); // A global removal accepts multiple executables, like `deno uninstall // --global`. let r = flags_from_vec(svec!["deno", "remove", "-g", "file_server", "chalk"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Uninstall(UninstallFlags { kind: UninstallKind::Global(UninstallFlagsGlobal { packages: svec!["file_server", "chalk"], root: None, }), }), ..Flags::default() } ); // `--root` requires `--global`. let r = flags_from_vec(svec!["deno", "remove", "--root", "/tmp", "@std/path"]); assert!(r.is_err()); // Without `--global`, removal stays a config-file dependency removal. let r = flags_from_vec(svec!["deno", "remove", "@david/which"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Remove(RemoveFlags { packages: svec!["@david/which"], lockfile_only: false, package_json: false, }), ..Flags::default() } ); } #[test] fn run_with_frozen_lockfile() { let cases = [ (Some("--frozen"), Some(true)), (Some("--frozen=true"), Some(true)), (Some("--frozen=false"), Some(false)), (None, None), ]; for (flag, frozen) in cases { let mut args = svec!["deno", "run"]; if let Some(f) = flag { args.push(f.into()); } args.push("script.ts".into()); let r = flags_from_vec(args); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags::new_default( "script.ts".to_string(), )), frozen_lockfile: frozen, code_cache_enabled: true, ..Flags::default() } ); } } #[test] fn allow_scripts() { let cases = [ (Some("--allow-scripts"), Ok(PackagesAllowedScripts::All)), (None, Ok(PackagesAllowedScripts::None)), ( Some("--allow-scripts=npm:foo"), Ok(PackagesAllowedScripts::Some(vec![ PackageReq::from_str("foo").unwrap(), ])), ), ( Some("--allow-scripts=npm:foo,npm:bar@2"), Ok(PackagesAllowedScripts::Some(vec![ PackageReq::from_str("foo").unwrap(), PackageReq::from_str("bar@2").unwrap(), ])), ), (Some("--allow-scripts=foo"), Err("Invalid package")), ( Some("--allow-scripts=npm:foo@next"), Err("Tags are not supported in --allow-scripts: npm:foo@next"), ), ( Some("--allow-scripts=jsr:@foo/bar"), Err("An 'npm:' specifier is required"), ), ]; for (flag, value) in cases { let mut args = svec!["deno", "cache"]; if let Some(flag) = flag { args.push(flag.into()); } args.push("script.ts".into()); let r = flags_from_vec(args); match value { Ok(value) => { assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Cache(CacheFlags { files: svec!["script.ts"], }), allow_scripts: value, ..Flags::default() } ); } Err(e) => { let err = r.unwrap_err(); assert!( err.to_string().contains(e), "expected to contain '{e}' got '{err}'" ); } } } } #[test] fn x_ignore_scripts() { let flags = flags_from_vec(svec![ "deno", "x", "--ignore-scripts", "-y", "npm:foo", "--bar" ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::X(XFlags { kind: XFlagsKind::Command(XCommandFlags { yes: true, command: "npm:foo".to_string(), ignore_scripts: PackagesAllowedScripts::All, package: None, }), }), argv: svec!["--bar"], permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); let err = flags_from_vec(svec![ "deno", "x", "--ignore-scripts", "--allow-scripts", "npm:foo" ]) .unwrap_err(); assert!(err.to_string().contains("cannot be used with")); let flags = flags_from_vec(svec![ "deno", "x", "--ignore-scripts=foo,npm:bar@2", "npm:foo" ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::X(XFlags { kind: XFlagsKind::Command(XCommandFlags { yes: false, command: "npm:foo".to_string(), ignore_scripts: PackagesAllowedScripts::Some(vec![ PackageReq::from_str("foo").unwrap(), PackageReq::from_str("bar@2").unwrap(), ]), package: None, }), }), permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn bare_run() { let r = flags_from_vec(svec!["deno", "--no-config", "script.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), bare: true, coverage_dir: None, print_task_list: false, }), config_flag: ConfigFlag::Disabled, code_cache_enabled: true, ..Flags::default() } ); } #[test] fn bare_global() { let r = flags_from_vec(svec!["deno", "--log-level=debug"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None, is_default_command: true, json: false, }), log_level: Some(Level::Debug), permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn repl_user_args() { let r = flags_from_vec(svec!["deno", "repl", "foo"]); assert!(r.is_err()); let r = flags_from_vec(svec!["deno", "repl", "--", "foo"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None, is_default_command: false, json: false, }), argv: svec!["foo"], ..Flags::default() } ); } #[test] fn bare_with_flag_no_file() { let r = flags_from_vec(svec!["deno", "--no-config"]); let err = r.unwrap_err(); assert!(err.to_string().contains("error: [SCRIPT_ARG] may only be omitted with --v8-flags=--help, else to use the repl with arguments, please use the `deno repl` subcommand")); assert!( err .to_string() .contains("Usage: deno [OPTIONS] [COMMAND] [SCRIPT_ARG]...") ); } // PORT-SKIP: subcommands_recognized_by_node_shim depends on the node_shim crate; kept in cli/args/flags.rs // PORT-SKIP: equal_help_output walks the whole CommandDef tree; kept in cli/args/flags.rs #[test] fn ci_subcommand_defaults() { let r = flags_from_vec(svec!["deno", "ci"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Ci(CiFlags::default()), frozen_lockfile: Some(true), ..Flags::default() } ); } #[test] fn ci_subcommand_prod_skip_types() { let r = flags_from_vec(svec!["deno", "ci", "--prod", "--skip-types"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Ci(CiFlags { production: true, skip_types: true, }), frozen_lockfile: Some(true), ..Flags::default() } ); } #[test] fn install_permissions_non_global() { let r = flags_from_vec(svec!["deno", "install", "--allow-net", "jsr:@std/fs"]); assert!( r.unwrap_err() .to_string() .contains("Note: Permission flags can only be used in a global setting") ); } // PORT-SKIP: install_os_arch_flags asserts DenoSubcommand::npm_system_info (cli-only); kept in cli/args/flags.rs // PORT-SKIP: install_os_only_flag asserts DenoSubcommand::npm_system_info (cli-only); kept in cli/args/flags.rs #[test] fn install_os_arch_conflicts_with_global() { let r = flags_from_vec(svec!["deno", "install", "-g", "--os", "linux", "mod.ts"]); assert!(r.is_err()); } #[test] fn install_production() { let r = flags_from_vec(svec!["deno", "install", "--prod"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Install(InstallFlags::Local( InstallFlagsLocal::TopLevel(InstallTopLevelFlags { lockfile_only: false, production: true, skip_types: false, }), NpmInstallTargetFlags::default(), )), ..Flags::default() } ); } #[test] fn install_production_with_entrypoint() { let r = flags_from_vec(svec![ "deno", "install", "--prod", "--entrypoint", "main.ts" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Install(InstallFlags::Local( InstallFlagsLocal::Entrypoints(InstallEntrypointsFlags { entrypoints: svec!["main.ts"], lockfile_only: false, production: true, skip_types: false, }), NpmInstallTargetFlags::default(), )), ..Flags::default() } ); } #[test] fn install_production_with_skip_types() { let r = flags_from_vec(svec!["deno", "install", "--prod", "--skip-types"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Install(InstallFlags::Local( InstallFlagsLocal::TopLevel(InstallTopLevelFlags { lockfile_only: false, production: true, skip_types: true, }), NpmInstallTargetFlags::default(), )), ..Flags::default() } ); } #[test] fn install_skip_types_requires_prod() { let r = flags_from_vec(svec!["deno", "install", "--skip-types"]); assert!(r.is_err()); } #[test] fn install_production_conflicts_with_global() { let r = flags_from_vec(svec![ "deno", "install", "--prod", "--global", "jsr:@std/http/file-server" ]); assert!(r.is_err()); } #[test] fn install_production_conflicts_with_dev() { let r = flags_from_vec(svec!["deno", "install", "--prod", "--dev", "npm:chalk"]); assert!(r.is_err()); } #[test] fn jupyter_unstable_flags() { let r = flags_from_vec(svec![ "deno", "jupyter", "--unstable-ffi", "--unstable-bare-node-builtins", "--unstable-worker-options" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Jupyter(JupyterFlags { install: false, kernel: false, conn_file: None, name: None, display: None, force: false, }), unstable_config: UnstableConfig { sloppy_imports: false, features: svec!["bare-node-builtins", "ffi", "worker-options"], ..Default::default() }, ..Flags::default() } ); } #[test] fn serve_with_allow_all() { let r = flags_from_vec(svec!["deno", "serve", "--allow-all", "./main.ts"]); let flags = r.unwrap(); assert_eq!( &flags, &Flags { subcommand: DenoSubcommand::Serve(ServeFlags::new_default( "./main.ts".into(), 8000, "0.0.0.0" )), permissions: PermissionFlags { allow_all: true, allow_net: None, ..Default::default() }, code_cache_enabled: true, ..Default::default() } ); } #[test] fn escape_and_split_commas_test() { assert_eq!(escape_and_split_commas("foo".to_string()).unwrap(), ["foo"]); assert!(escape_and_split_commas("foo,".to_string()).is_err()); assert_eq!( escape_and_split_commas("foo,,".to_string()).unwrap(), ["foo,"] ); assert!(escape_and_split_commas("foo,,,".to_string()).is_err()); assert_eq!( escape_and_split_commas("foo,,,,".to_string()).unwrap(), ["foo,,"] ); assert_eq!( escape_and_split_commas("foo,bar".to_string()).unwrap(), ["foo", "bar"] ); assert_eq!( escape_and_split_commas("foo,,bar".to_string()).unwrap(), ["foo,bar"] ); assert_eq!( escape_and_split_commas("foo,,,bar".to_string()).unwrap(), ["foo,", "bar"] ); } #[test] fn net_flag_with_url() { let r = flags_from_vec(svec![ "deno", "run", "--allow-net=https://example.com", "script.ts" ]); assert_eq!( r.unwrap_err().to_string(), "error: invalid value 'https://example.com': URLs are not supported, only domains and IPs" ); } #[test] fn node_modules_dir_default() { let r = flags_from_vec(svec!["deno", "run", "--node-modules-dir", "./foo.ts"]); let flags = r.unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "./foo.ts".into(), ..Default::default() }), node_modules_dir: Some(NodeModulesDirMode::Auto), code_cache_enabled: true, ..Default::default() } ) } #[test] fn flag_before_subcommand() { // PORT-NOTE: clap appends a `Usage: deno repl [OPTIONS] [-- [ARGS]...]` // trailer; the new parser reports only the error and the tip. let r = flags_from_vec(svec!["deno", "--allow-net", "repl"]); assert_eq!( r.unwrap_err().to_string(), "error: unexpected argument '--allow-net' found tip: 'repl --allow-net' exists" ) } #[test] fn flag_before_subcommand_not_supported() { // `lint` does not accept `--allow-all`, so the error must not claim that // `lint --allow-all` exists (see issue #27336). // PORT-NOTE: clap appends a `Usage: deno lint [OPTIONS] [files]...` // trailer; the new parser reports only the error. let r = flags_from_vec(svec!["deno", "--allow-all", "lint"]); assert_eq!( r.unwrap_err().to_string(), "error: unexpected argument '--allow-all' found" ) } #[test] fn allow_all_conflicts_allow_perms() { let flags = [ "--allow-read", "--allow-write", "--allow-net", "--allow-env", "--allow-run", "--allow-sys", "--allow-ffi", "--allow-import", ]; for flag in flags { let r = flags_from_vec(svec!["deno", "run", "--allow-all", flag, "foo.ts"]); assert!(r.is_err()); } } #[test] fn allow_import_with_url() { let r = flags_from_vec(svec![ "deno", "run", "--allow-import=https://example.com", "script.ts" ]); assert_eq!( r.unwrap_err().to_string(), "error: invalid value 'https://example.com': URLs are not supported, only domains and IPs" ); } #[test] fn deny_import_with_url() { let r = flags_from_vec(svec![ "deno", "run", "--deny-import=https://example.com", "script.ts", ]); assert_eq!( r.unwrap_err().to_string(), "error: invalid value 'https://example.com': URLs are not supported, only domains and IPs" ); } #[test] fn outdated_subcommand() { let cases = [ ( svec![], OutdatedFlags { filters: vec![], kind: OutdatedKind::PrintOutdated { compatible: false }, recursive: false, }, ), ( svec!["--recursive"], OutdatedFlags { filters: vec![], kind: OutdatedKind::PrintOutdated { compatible: false }, recursive: true, }, ), ( svec!["--recursive", "--compatible"], OutdatedFlags { filters: vec![], kind: OutdatedKind::PrintOutdated { compatible: true }, recursive: true, }, ), ( svec!["--update"], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: false, }, recursive: false, }, ), ( svec!["--update", "--latest"], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: true, interactive: false, lockfile_only: false, }, recursive: false, }, ), ( svec!["--update", "--recursive"], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: false, }, recursive: true, }, ), ( svec!["--update", "--lockfile-only"], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: true, }, recursive: false, }, ), ( svec!["--update", "@foo/bar"], OutdatedFlags { filters: svec!["@foo/bar"], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: false, }, recursive: false, }, ), ( svec!["--latest"], OutdatedFlags { filters: svec![], kind: OutdatedKind::PrintOutdated { compatible: false }, recursive: false, }, ), ( svec!["--update", "--latest", "--interactive"], OutdatedFlags { filters: svec![], kind: OutdatedKind::Update { latest: true, interactive: true, lockfile_only: false, }, recursive: false, }, ), ]; for (input, expected) in cases { let mut args = svec!["deno", "outdated"]; args.extend(input); let r = flags_from_vec(args.clone()).unwrap(); assert_eq!( r.subcommand, DenoSubcommand::Outdated(expected), "incorrect result for args: {:?}", args ); } } #[test] fn list_subcommand() { let cases = [ (svec![], ListFlags::default()), ( svec!["--recursive"], ListFlags { recursive: true, ..Default::default() }, ), ( svec!["--depth", "3"], ListFlags { depth: 3, ..Default::default() }, ), ( svec!["--prod"], ListFlags { prod: true, ..Default::default() }, ), ( svec!["--dev", "@foo/bar", "react*"], ListFlags { dev: true, filters: svec!["@foo/bar", "react*"], ..Default::default() }, ), ]; for (input, expected) in cases { let mut args = svec!["deno", "list"]; args.extend(input); let r = flags_from_vec(args.clone()).unwrap(); assert_eq!( r.subcommand, DenoSubcommand::List(expected), "incorrect result for args: {:?}", args ); } // --prod and --dev are mutually exclusive assert!(flags_from_vec(svec!["deno", "list", "--prod", "--dev"]).is_err()); } #[test] fn update_subcommand() { let cases = [ ( svec![], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: false, }, recursive: false, }, ), ( svec!["--latest"], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: true, interactive: false, lockfile_only: false, }, recursive: false, }, ), ( svec!["--recursive"], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: false, }, recursive: true, }, ), ( svec!["--lockfile-only"], OutdatedFlags { filters: vec![], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: true, }, recursive: false, }, ), ( svec!["@foo/bar"], OutdatedFlags { filters: svec!["@foo/bar"], kind: OutdatedKind::Update { latest: false, interactive: false, lockfile_only: false, }, recursive: false, }, ), ( svec!["--latest", "--interactive"], OutdatedFlags { filters: svec![], kind: OutdatedKind::Update { latest: true, interactive: true, lockfile_only: false, }, recursive: false, }, ), ]; for (input, expected) in cases { let mut args = svec!["deno", "update"]; args.extend(input); let r = flags_from_vec(args.clone()).unwrap(); assert_eq!( r.subcommand, DenoSubcommand::Outdated(expected), "incorrect result for args: {:?}", args ); } } #[test] fn update_subcommand_frozen_flag() { let r = flags_from_vec(svec!["deno", "update", "--frozen=false"]).unwrap(); assert_eq!(r.frozen_lockfile, Some(false)); let r = flags_from_vec(svec!["deno", "update", "--frozen"]).unwrap(); assert_eq!(r.frozen_lockfile, Some(true)); } #[test] fn outdated_subcommand_frozen_flag() { let r = flags_from_vec(svec!["deno", "outdated", "--frozen=false"]).unwrap(); assert_eq!(r.frozen_lockfile, Some(false)); } #[test] fn approve_scripts_subcommand() { let cases = [ ( svec![], ApproveScriptsFlags { packages: vec![], lockfile_only: false, }, ), ( svec!["npm:pkg@1.0.0"], ApproveScriptsFlags { packages: vec!["npm:pkg@1.0.0".to_string()], lockfile_only: false, }, ), ( svec!["npm:pkg1@1.0.0", "npm:pkg2@2.0.0"], ApproveScriptsFlags { packages: vec![ "npm:pkg1@1.0.0".to_string(), "npm:pkg2@2.0.0".to_string(), ], lockfile_only: false, }, ), ( svec!["npm:pkg1@1.0.0,npm:pkg2@2.0.0"], ApproveScriptsFlags { packages: vec![ "npm:pkg1@1.0.0".to_string(), "npm:pkg2@2.0.0".to_string(), ], lockfile_only: false, }, ), ( svec!["--lockfile-only"], ApproveScriptsFlags { packages: vec![], lockfile_only: true, }, ), ( svec!["--lockfile-only", "npm:pkg@1.0.0"], ApproveScriptsFlags { packages: vec!["npm:pkg@1.0.0".to_string()], lockfile_only: true, }, ), ( svec!["npm:pkg@1.0.0", "--lockfile-only"], ApproveScriptsFlags { packages: vec!["npm:pkg@1.0.0".to_string()], lockfile_only: true, }, ), ( svec!["npm:pkg1@1.0.0", "npm:pkg2@2.0.0", "--lockfile-only"], ApproveScriptsFlags { packages: vec![ "npm:pkg1@1.0.0".to_string(), "npm:pkg2@2.0.0".to_string(), ], lockfile_only: true, }, ), ]; for (input, expected) in cases { let mut args = svec!["deno", "approve-scripts"]; args.extend(input); let r = flags_from_vec(args.clone()).unwrap(); assert_eq!( r.subcommand, DenoSubcommand::ApproveScripts(expected), "incorrect result for args: {:?}", args ); } } #[test] fn clean_subcommand() { let cases = [ ( svec![], CleanFlags { except_paths: vec![], dry_run: false, }, ), ( svec!["--except", "path1"], CleanFlags { except_paths: vec!["path1".to_string()], dry_run: false, }, ), ( svec!["--except", "path1", "path2"], CleanFlags { except_paths: vec!["path1".to_string(), "path2".to_string()], dry_run: false, }, ), ( svec!["--except", "path1", "--dry-run"], CleanFlags { except_paths: vec!["path1".to_string()], dry_run: true, }, ), ( svec!["--dry-run"], CleanFlags { except_paths: vec![], dry_run: true, }, ), ]; for (input, expected) in cases { // `--except` builds a module graph of the retained paths, so it parses // with `--cached-only`; other invocations (e.g. `--dry-run` alone) don't. let cached_only = input.iter().any(|arg| arg == "--except"); let mut args = svec!["deno", "clean"]; args.extend(input); let r = flags_from_vec(args.clone()) .inspect_err(|e| { #[allow(clippy::print_stderr, reason = "actually want to output")] { eprintln!("error: {:?} on input: {:?}", e, args); } }) .unwrap(); assert_eq!( r, Flags { subcommand: DenoSubcommand::Clean(expected), cached_only, ..Flags::default() }, "incorrect result for args: {:?}", args ); } } #[test] fn conditions_test() { let flags = flags_from_vec(svec![ "deno", "run", "--conditions", "development", "main.ts" ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "main.ts".into(), ..Default::default() }), node_conditions: svec!["development"], code_cache_enabled: true, ..Default::default() } ); let flags = flags_from_vec(svec![ "deno", "run", "--conditions", "development,production", "main.ts" ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "main.ts".into(), ..Default::default() }), node_conditions: svec!["development", "production"], code_cache_enabled: true, ..Default::default() } ); let flags = flags_from_vec(svec![ "deno", "run", "--conditions", "development", "--conditions", "production", "main.ts" ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "main.ts".into(), ..Default::default() }), node_conditions: svec!["development", "production"], code_cache_enabled: true, ..Default::default() } ); } #[test] fn preload_flag_test() { let flags = flags_from_vec(svec!["deno", "run", "--preload", "preload.js", "main.ts"]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "main.ts".into(), ..Default::default() }), preload: svec!["preload.js"], code_cache_enabled: true, ..Default::default() } ); let flags = flags_from_vec(svec!["deno", "run", "--preload", "data:,()", "main.ts"]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "main.ts".into(), ..Default::default() }), preload: svec!["data:,()"], code_cache_enabled: true, ..Default::default() } ); let flags = flags_from_vec(svec![ "deno", "compile", "--preload", "p1.js", "--preload", "./p2.js", "main.ts" ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Compile(CompileFlags { source_file: "main.ts".into(), output: None, args: vec![], target: None, no_terminal: false, icon: None, include: Default::default(), exclude: Default::default(), eszip: false, self_extracting: false, bundle: false, app_name: None, minify: false, exclude_unused_npm: false, engine: Default::default(), }), type_check_mode: TypeCheckMode::Local, preload: svec!["p1.js", "./p2.js"], code_cache_enabled: true, ..Default::default() } ); let flags = flags_from_vec(svec![ "deno", "test", "--preload", "p1.js", "--import", "./p2.js", ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Test(TestFlags::default()), preload: svec!["p1.js", "./p2.js"], type_check_mode: TypeCheckMode::Local, code_cache_enabled: false, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, ..Default::default() } ); let flags = flags_from_vec(svec![ "deno", "bench", "--preload", "p1.js", "--import", "./p2.js", ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Bench(BenchFlags::default()), preload: svec!["p1.js", "./p2.js"], type_check_mode: TypeCheckMode::Local, code_cache_enabled: false, permissions: PermissionFlags { no_prompt: true, ..Default::default() }, ..Default::default() } ); } #[test] fn require_flag_test() { let flags = flags_from_vec(svec!["deno", "run", "--require", "require.js", "main.ts"]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "main.ts".into(), ..Default::default() }), require: svec!["require.js"], code_cache_enabled: true, ..Default::default() } ); let flags = flags_from_vec(svec![ "deno", "run", "--require", "r1.js", "--require", "./r2.js", "main.ts" ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "main.ts".into(), ..Default::default() }), require: svec!["r1.js", "./r2.js"], code_cache_enabled: true, ..Default::default() } ); } #[test] fn check_with_v8_flags() { let flags = flags_from_vec(svec!["deno", "check", "--v8-flags=--help", "script.ts",]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Check(CheckFlags { files: svec!["script.ts"], doc: false, doc_only: false, check_js: false, }), type_check_mode: TypeCheckMode::Local, code_cache_enabled: true, v8_flags: svec!["--help"], ..Flags::default() } ); } #[test] fn multiple_allow_all() { let flags = flags_from_vec(svec![ "deno", "run", "--allow-all", "--inspect", "-A", "script.ts", ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), ..Default::default() }), inspect: Some("127.0.0.1:9229".parse().unwrap()), code_cache_enabled: true, permissions: PermissionFlags { allow_all: true, ..Default::default() }, ..Flags::default() } ); } #[test] fn inspect_flag_parsing() { use std::net::IpAddr; use std::net::Ipv4Addr; let cases = vec![ ( "127.0.0.1:9229", SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9229), ), ( "192.168.0.1", SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1)), 9229), ), ( "10000", SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 10000), ), ( ":10000", SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 10000), ), ( ":0", SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0), ), ( "0", SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0), ), ]; for case in cases { let flags = flags_from_vec(svec![ "deno", "run", &format!("--inspect={}", case.0), "script.ts", ]) .unwrap(); assert_eq!( flags, Flags { subcommand: DenoSubcommand::Run(RunFlags { script: "script.ts".to_string(), ..Default::default() }), inspect: Some(case.1), code_cache_enabled: true, ..Flags::default() } ); } } /// Node accepts `--inspect=localhost:0` (and similar hostname forms); the /// parser resolves them via DNS rather than rejecting or silently dropping /// them. Ported from the clap-era `inspect_value_parser_resolves_hostnames`. #[test] fn inspect_resolves_hostnames() { let cases = [ ("localhost:0", 0u16), ("localhost:1234", 1234), ("localhost", 9229), ]; for (input, expected_port) in cases { let flags = flags_from_vec(svec![ "deno", "run", &format!("--inspect={input}"), "foo.ts" ]) .unwrap_or_else(|e| panic!("failed to parse {input:?}: {e}")); let addr = flags .inspect .unwrap_or_else(|| panic!("--inspect={input} did not set an address")); assert_eq!(addr.port(), expected_port, "port for {input:?}"); assert!( addr.ip().is_loopback(), "expected loopback for {input:?}, got {}", addr.ip() ); } } /// Non-hostname forms: bare port, bare `:port`, bare IP, and full `host:port`. #[test] fn inspect_address_forms() { let cases = [ ("9222", "127.0.0.1:9222"), (":9222", "127.0.0.1:9222"), ("192.168.0.1", "192.168.0.1:9229"), ("192.168.0.1:8080", "192.168.0.1:8080"), ("[::1]:8080", "[::1]:8080"), ]; for (input, expected) in cases { let flags = flags_from_vec(svec![ "deno", "run", &format!("--inspect={input}"), "foo.ts" ]) .unwrap_or_else(|e| panic!("failed to parse {input:?}: {e}")); assert_eq!( flags.inspect.map(|a| a.to_string()).as_deref(), Some(expected), "{input:?}" ); } } /// An unresolvable host or out-of-range port is rejected outright rather than /// silently dropping the flag. #[test] fn inspect_invalid_address_is_rejected() { for input in ["localhost:99999", ""] { let r = flags_from_vec(svec![ "deno", "run", &format!("--inspect={input}"), "foo.ts" ]); assert!(r.is_err(), "expected --inspect={input:?} to be rejected"); } } #[test] fn inspect_publish_uid_flag_parsing() { // Test with both stderr and http let flags = flags_from_vec(svec![ "deno", "run", "--inspect", "--inspect-publish-uid=stderr,http", "script.ts", ]) .unwrap(); assert_eq!( flags.inspect_publish_uid, Some(InspectPublishUid { console: true, http: true, }) ); // Test with only stderr let flags = flags_from_vec(svec![ "deno", "run", "--inspect", "--inspect-publish-uid=stderr", "script.ts", ]) .unwrap(); assert_eq!( flags.inspect_publish_uid, Some(InspectPublishUid { console: true, http: false, }) ); // Test with only http let flags = flags_from_vec(svec![ "deno", "run", "--inspect", "--inspect-publish-uid=http", "script.ts", ]) .unwrap(); assert_eq!( flags.inspect_publish_uid, Some(InspectPublishUid { console: false, http: true, }) ); // Test without the flag (should be None) let flags = flags_from_vec(svec!["deno", "run", "--inspect", "script.ts",]).unwrap(); assert_eq!(flags.inspect_publish_uid, None); } #[test] fn node_options_require() { // Test NODE_OPTIONS --require when no CLI --require is passed set_test_node_options(Some("--require only.js")); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!(flags.require, vec!["only.js"]); } #[test] fn node_options_require_prepend_to_cli() { // Test NODE_OPTIONS --require is prepended to CLI --require values set_test_node_options(Some("--require foo.js --require bar.js")); let flags = flags_from_vec(svec!["deno", "run", "--require", "cli.js", "script.ts",]) .unwrap(); set_test_node_options(None); assert_eq!(flags.require, vec!["foo.js", "bar.js", "cli.js"]); } #[test] fn node_options_inspect_publish_uid() { set_test_node_options(Some("--inspect-publish-uid=http")); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!( flags.inspect_publish_uid, Some(InspectPublishUid { console: false, http: true, }) ); } #[test] fn node_options_inspect_publish_uid_cli_precedence() { set_test_node_options(Some("--inspect-publish-uid=http")); let flags = flags_from_vec(svec![ "deno", "run", "--inspect-publish-uid=stderr", "script.ts", ]) .unwrap(); set_test_node_options(None); assert_eq!( flags.inspect_publish_uid, Some(InspectPublishUid { console: true, http: false, }) ); } #[test] fn node_options_inspect() { set_test_node_options(Some("--inspect=127.0.0.1:9333")); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!(flags.inspect, Some("127.0.0.1:9333".parse().unwrap())); } #[test] fn node_options_inspect_default_address() { set_test_node_options(Some("--inspect")); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!(flags.inspect, Some("127.0.0.1:9229".parse().unwrap())); } #[test] fn node_options_inspect_brk_and_wait() { set_test_node_options(Some("--inspect-brk=127.0.0.1:9334")); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!(flags.inspect_brk, Some("127.0.0.1:9334".parse().unwrap())); set_test_node_options(Some("--inspect-wait=127.0.0.1:9335")); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!(flags.inspect_wait, Some("127.0.0.1:9335".parse().unwrap())); } #[test] fn node_options_inspect_cli_precedence() { // An explicit CLI inspector flag suppresses the entire NODE_OPTIONS // inspector family, since --inspect/-brk/-wait are mutually exclusive. set_test_node_options(Some("--inspect=127.0.0.1:9333")); let flags = flags_from_vec(svec![ "deno", "run", "--inspect-brk=127.0.0.1:9444", "script.ts", ]) .unwrap(); set_test_node_options(None); assert_eq!(flags.inspect, None); assert_eq!(flags.inspect_brk, Some("127.0.0.1:9444".parse().unwrap())); } #[test] fn node_options_combined() { // Test NODE_OPTIONS with both --require and --inspect-publish-uid set_test_node_options(Some( "--require foo.js --inspect-publish-uid=stderr,http", )); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!(flags.require, vec!["foo.js"]); assert_eq!( flags.inspect_publish_uid, Some(InspectPublishUid { console: true, http: true, }) ); } #[test] fn node_options_empty() { set_test_node_options(Some("")); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert!(flags.require.is_empty()); assert_eq!(flags.inspect_publish_uid, None); } #[test] fn node_options_ignores_unknown_flags() { set_test_node_options(Some( "--require known.js --unknown-flag --another-unknown", )); let flags = flags_from_vec(svec!["deno", "run", "script.ts",]).unwrap(); set_test_node_options(None); assert_eq!(flags.require, vec!["known.js"]); } #[test] fn bump_version_patch() { let r = flags_from_vec(svec!["deno", "bump-version", "patch"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Patch), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_minor() { let r = flags_from_vec(svec!["deno", "bump-version", "minor"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Minor), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_major() { let r = flags_from_vec(svec!["deno", "bump-version", "major"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Major), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_prerelease() { let r = flags_from_vec(svec!["deno", "bump-version", "prerelease"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Prerelease), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_premajor() { let r = flags_from_vec(svec!["deno", "bump-version", "premajor"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Premajor), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_preminor() { let r = flags_from_vec(svec!["deno", "bump-version", "preminor"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Preminor), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_prepatch() { let r = flags_from_vec(svec!["deno", "bump-version", "prepatch"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Prepatch), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_no_args() { let r = flags_from_vec(svec!["deno", "bump-version"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags::default()), ..Flags::default() } ); } #[test] fn bump_version_invalid_increment() { let r = flags_from_vec(svec!["deno", "bump-version", "invalid"]); assert!(r.is_err()); } #[test] fn bump_version_workspace_flags() { let r = flags_from_vec(svec![ "deno", "bump-version", "--workspace", "--dry-run", "--start", "v1.0.0", "--base", "main", "--import-map", "import_map.json", "--release-notes", "Releases.md", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: None, workspace: Some(true), dry_run: true, start: Some("v1.0.0".to_string()), base: Some("main".to_string()), import_map: Some("import_map.json".to_string()), release_notes: Some("Releases.md".to_string()), config: None, }), ..Flags::default() } ); } #[test] fn bump_version_config() { let r = flags_from_vec(svec![ "deno", "bump-version", "patch", "--config", "package.json", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Patch), config: Some("package.json".to_string()), ..Default::default() }), ..Flags::default() } ); let r = flags_from_vec(svec![ "deno", "bump-version", "-c", "package.json", "patch", ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Patch), config: Some("package.json".to_string()), ..Default::default() }), ..Flags::default() } ); } #[test] fn bump_version_no_workspace() { let r = flags_from_vec(svec!["deno", "bump-version", "patch", "--no-workspace"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::BumpVersion(VersionFlags { increment: Some(VersionIncrement::Patch), workspace: Some(false), ..Default::default() }), ..Flags::default() } ); } #[test] fn why_package() { let r = flags_from_vec(svec!["deno", "why", "express"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Why(WhyFlags { package: "express".to_string(), }), ..Flags::default() } ); } #[test] fn why_package_with_version() { let r = flags_from_vec(svec!["deno", "why", "express@4.18.2"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Why(WhyFlags { package: "express@4.18.2".to_string(), }), ..Flags::default() } ); } #[test] fn why_scoped_package() { let r = flags_from_vec(svec!["deno", "why", "@scope/pkg"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Why(WhyFlags { package: "@scope/pkg".to_string(), }), ..Flags::default() } ); } #[test] fn why_scoped_package_with_version() { let r = flags_from_vec(svec!["deno", "why", "@scope/pkg@1.0.0"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Why(WhyFlags { package: "@scope/pkg@1.0.0".to_string(), }), ..Flags::default() } ); } #[test] fn why_missing_package() { let r = flags_from_vec(svec!["deno", "why"]); assert!(r.is_err()); } #[test] fn audit_basic() { let r = flags_from_vec(svec!["deno", "audit"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Audit(AuditFlags { severity: "low".to_string(), dev: true, prod: true, optional: true, ..Default::default() }), ..Flags::default() } ); } #[test] fn audit_fix_flag() { let r = flags_from_vec(svec!["deno", "audit", "--fix"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Audit(AuditFlags { severity: "low".to_string(), dev: true, prod: true, optional: true, fix: true, ..Default::default() }), ..Flags::default() } ); } #[test] fn audit_fix_positional() { let r = flags_from_vec(svec!["deno", "audit", "fix"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Audit(AuditFlags { severity: "low".to_string(), dev: true, prod: true, optional: true, fix: true, ..Default::default() }), ..Flags::default() } ); } #[test] fn audit_invalid_positional() { let r = flags_from_vec(svec!["deno", "audit", "bogus"]); assert!(r.is_err()); } #[test] fn transpile_single_file() { let r = flags_from_vec(svec!["deno", "transpile", "main.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts"], output: None, output_dir: None, declaration: false, source_map: SourceMapMode::None, }), ..Flags::default() } ); } #[test] fn transpile_multiple_files() { let r = flags_from_vec(svec!["deno", "transpile", "main.ts", "helpers.ts"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts", "helpers.ts"], output: None, output_dir: None, declaration: false, source_map: SourceMapMode::None, }), ..Flags::default() } ); } #[test] fn transpile_with_output() { let r = flags_from_vec(svec!["deno", "transpile", "main.ts", "-o", "out.js"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts"], output: Some("out.js".to_string()), output_dir: None, declaration: false, source_map: SourceMapMode::None, }), ..Flags::default() } ); } #[test] fn transpile_with_outdir() { let r = flags_from_vec(svec!["deno", "transpile", "main.ts", "--outdir", "dist"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts"], output: None, output_dir: Some("dist".to_string()), declaration: false, source_map: SourceMapMode::None, }), ..Flags::default() } ); } #[test] fn transpile_with_source_map_inline() { let r = flags_from_vec(svec![ "deno", "transpile", "main.ts", "--source-map", "inline" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts"], output: None, output_dir: None, declaration: false, source_map: SourceMapMode::Inline, }), ..Flags::default() } ); } #[test] fn transpile_with_source_map_separate() { let r = flags_from_vec(svec![ "deno", "transpile", "main.ts", "--source-map", "separate" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts"], output: None, output_dir: None, declaration: false, source_map: SourceMapMode::Separate, }), ..Flags::default() } ); } #[test] fn transpile_with_declaration() { let r = flags_from_vec(svec!["deno", "transpile", "main.ts", "--declaration"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts"], output: None, output_dir: None, declaration: true, source_map: SourceMapMode::None, }), ..Flags::default() } ); } #[test] fn transpile_all_flags() { let r = flags_from_vec(svec![ "deno", "transpile", "main.ts", "-o", "out.js", "--source-map", "separate", "--declaration" ]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Transpile(TranspileFlags { files: svec!["main.ts"], output: Some("out.js".to_string()), output_dir: None, declaration: true, source_map: SourceMapMode::Separate, }), ..Flags::default() } ); } // --------------------------------------------------------------------------- // Tier-1 functionality-parity tests: flags that existed in clap but were // missing/unreachable in the new parser (see the parity audit). // --------------------------------------------------------------------------- #[test] fn tier1_test_sanitize_and_snapshot_flags() { let flags = flags_from_vec(svec![ "deno", "test", "--sanitize-ops", "--sanitize-resources", "--coverage=cov", "--coverage-threshold=80", "--update-snapshots" ]) .unwrap(); let DenoSubcommand::Test(t) = flags.subcommand else { unreachable!() }; assert!(t.sanitize_ops); assert!(t.sanitize_resources); assert_eq!(t.coverage_threshold, Some(80)); assert!(t.update_snapshots); // -u is short for --update-snapshots. let flags = flags_from_vec(svec!["deno", "test", "-u"]).unwrap(); assert!( matches!(flags.subcommand, DenoSubcommand::Test(t) if t.update_snapshots) ); // --coverage-threshold requires --coverage. assert!( flags_from_vec(svec!["deno", "test", "--coverage-threshold=80"]).is_err() ); } #[test] fn tier1_compile_bundle_flags() { let flags = flags_from_vec(svec![ "deno", "compile", "--bundle", "--minify", "--app-name=MyApp", "--exclude-unused-npm", "main.ts" ]) .unwrap(); let DenoSubcommand::Compile(c) = flags.subcommand else { unreachable!() }; assert!(c.bundle); assert!(c.minify); assert_eq!(c.app_name, Some("MyApp".to_string())); assert!(c.exclude_unused_npm); // --minify requires --bundle. assert!( flags_from_vec(svec!["deno", "compile", "--minify", "main.ts"]).is_err() ); } #[test] fn tier1_install_os_arch_package_json() { let flags = flags_from_vec(svec!["deno", "install", "--os", "linux", "--arch", "x64"]) .unwrap(); let DenoSubcommand::Install(InstallFlags::Local(_, npm_target)) = flags.subcommand else { unreachable!() }; assert_eq!(npm_target.os, Some("linux".to_string())); assert_eq!(npm_target.arch, Some("x64".to_string())); let flags = flags_from_vec(svec!["deno", "install", "--package-json", "npm:foo"]) .unwrap(); assert!(matches!( flags.subcommand, DenoSubcommand::Install(InstallFlags::Local( InstallFlagsLocal::Add(a), _ )) if a.package_json )); // --os / --arch conflict with --global. assert!( flags_from_vec(svec![ "deno", "install", "--global", "--os", "linux", "foo" ]) .is_err() ); } #[test] fn tier1_uninstall_remove_package_json() { let flags = flags_from_vec(svec!["deno", "uninstall", "--package-json", "foo"]) .unwrap(); assert!(matches!(flags.subcommand, DenoSubcommand::Uninstall(_))); let flags = flags_from_vec(svec!["deno", "remove", "--package-json", "foo"]).unwrap(); assert!(matches!(flags.subcommand, DenoSubcommand::Remove(_))); } #[test] fn tier1_check_flags() { let flags = flags_from_vec(svec!["deno", "check", "--no-code-cache", "main.ts"]) .unwrap(); assert!(!flags.code_cache_enabled); let flags = flags_from_vec(svec!["deno", "check", "--watch", "main.ts"]).unwrap(); assert!(flags.watch.is_some()); } #[test] fn tier1_serve_open_tunnel_hmr() { let flags = flags_from_vec(svec!["deno", "serve", "--open", "main.ts"]).unwrap(); assert!(matches!(&flags.subcommand, DenoSubcommand::Serve(s) if s.open_site)); let flags = flags_from_vec(svec!["deno", "serve", "--tunnel", "main.ts"]).unwrap(); assert!(flags.tunnel); // serve uses `--watch-hmr` (alias `--unstable-hmr`), not `--hmr`. for flag in ["--watch-hmr", "--unstable-hmr"] { let flags = flags_from_vec(svec!["deno", "serve", flag, "main.ts"]).unwrap(); assert!(flags.watch.map(|w| w.hmr).unwrap_or(false)); } } #[test] fn tier1_bundle_check_and_declaration() { let flags = flags_from_vec(svec!["deno", "bundle", "--check=all", "main.ts"]).unwrap(); assert_eq!(flags.type_check_mode, TypeCheckMode::All); let flags = flags_from_vec(svec!["deno", "bundle", "--declaration", "main.ts"]) .unwrap(); assert!( matches!(flags.subcommand, DenoSubcommand::Bundle(b) if b.declaration) ); } #[test] fn tier1_audit_why_lock_flags() { let flags = flags_from_vec(svec!["deno", "audit", "--lock=deno.lock", "--frozen"]) .unwrap(); assert!(matches!(&flags.subcommand, DenoSubcommand::Audit(_))); assert_eq!(flags.lock, Some("deno.lock".to_string())); assert_eq!(flags.frozen_lockfile, Some(true)); let flags = flags_from_vec(svec!["deno", "why", "--no-lock", "foo"]).unwrap(); assert!(matches!(&flags.subcommand, DenoSubcommand::Why(_))); assert!(flags.no_lock); } #[test] fn tier1_node_modules_linker() { let flags = flags_from_vec(svec![ "deno", "run", "--node-modules-linker=isolated", "main.ts" ]) .unwrap(); assert_eq!( flags.node_modules_linker, Some(NodeModulesLinkerMode::Isolated) ); // `--linker` is an alias. let flags = flags_from_vec(svec!["deno", "run", "--linker=hoisted", "main.ts"]) .unwrap(); assert_eq!( flags.node_modules_linker, Some(NodeModulesLinkerMode::Hoisted) ); } #[test] fn tier1_upgrade_x_env_file() { let flags = flags_from_vec(svec![ "deno", "upgrade", "--unsafely-ignore-certificate-errors=example.com" ]) .unwrap(); assert_eq!( flags.unsafely_ignore_certificate_errors, Some(svec!["example.com"]) ); let flags = flags_from_vec(svec!["deno", "x", "--package=npm:foo", "bar"]).unwrap(); assert!(matches!( flags.subcommand, DenoSubcommand::X(XFlags { kind: XFlagsKind::Command(c), }) if c.package == Some("npm:foo".to_string()) && c.command == "bar" )); for sub in ["outdated", "update", "publish"] { let flags = flags_from_vec(svec!["deno", sub, "--env-file=.env"]).unwrap(); assert_eq!(flags.env_file, Some(svec![".env"])); } } #[test] fn tier1_desktop_flags() { let flags = flags_from_vec(svec![ "deno", "desktop", "--output=MyApp.app", "--target=x86_64-unknown-linux-gnu", "--icon=icon.png", "--hmr", "--all-targets", "--backend=cef", "--include=extra.ts", "--exclude=skip.ts", "--compress=zstd", "--exclude-unused-npm", "--allow-read", "main.ts" ]) .unwrap(); assert_eq!(flags.type_check_mode, TypeCheckMode::Local); assert_eq!(flags.permissions.allow_read, Some(vec![])); let DenoSubcommand::Desktop(d) = flags.subcommand else { unreachable!() }; assert_eq!(d.output, Some("MyApp.app".to_string())); assert_eq!(d.target, Some("x86_64-unknown-linux-gnu".to_string())); assert_eq!(d.icon, Some(IconConfig::Single("icon.png".to_string()))); assert!(d.hmr); assert!(d.all_targets); assert_eq!(d.backend, Some("cef".to_string())); assert_eq!(d.compress, Some("zstd".to_string())); assert!(d.exclude_unused_npm); assert_eq!(d.include, svec!["extra.ts"]); assert_eq!(d.exclude, svec!["skip.ts"]); assert_eq!(d.source_file, "main.ts"); // Bare `deno desktop` defaults the entrypoint to ".". let flags = flags_from_vec(svec!["deno", "desktop"]).unwrap(); assert!( matches!(flags.subcommand, DenoSubcommand::Desktop(d) if d.source_file == ".") ); } // --------------------------------------------------------------------------- // Tier-2 value-validation parity: bad values now error at parse time instead // of being silently accepted/coerced (mirrors clap's value_parser). // --------------------------------------------------------------------------- #[test] fn tier2_choice_validation() { // Each of these passes an invalid value for a choice-restricted flag. let bad = [ svec!["deno", "test", "--reporter=bogus"], svec!["deno", "fmt", "--prose-wrap=bogus", "x.md"], svec!["deno", "fmt", "--ext=bogus", "x"], svec!["deno", "run", "--ext=bogus", "x.ts"], svec!["deno", "compile", "--target=bogus", "x.ts"], svec!["deno", "desktop", "--backend=bogus", "x.ts"], svec!["deno", "desktop", "--compress=bogus", "x.ts"], svec!["deno", "transpile", "--source-map=bogus", "x.ts"], svec!["deno", "completions", "bogus"], svec!["deno", "install", "--os", "bogus"], svec!["deno", "install", "--arch", "bogus"], svec!["deno", "audit", "--level=bogus"], svec!["deno", "--log-level=bogus", "run", "x.ts"], svec!["deno", "run", "--node-modules-dir=bogus", "x.ts"], svec!["deno", "run", "--node-modules-linker=bogus", "x.ts"], ]; for args in bad { assert!( flags_from_vec(args.clone()).is_err(), "expected error for {args:?}" ); } } #[test] fn tier2_numeric_validation() { let bad = [ svec!["deno", "run", "--seed=abc", "x.ts"], svec!["deno", "serve", "--port=abc", "x.ts"], svec!["deno", "serve", "--port=99999", "x.ts"], svec!["deno", "list", "--depth=abc"], svec!["deno", "test", "--retry=abc"], svec!["deno", "test", "--shuffle=abc"], svec!["deno", "test", "--coverage=cov", "--coverage-threshold=101"], svec!["deno", "fmt", "--line-width=0", "x.ts"], svec!["deno", "fmt", "--indent-width=0", "x.ts"], svec!["deno", "test", "--fail-fast=0"], ]; for args in bad { assert!( flags_from_vec(args.clone()).is_err(), "expected error for {args:?}" ); } // Valid numeric values still parse. assert!( flags_from_vec(svec!["deno", "serve", "--port=8080", "x.ts"]).is_ok() ); assert!( flags_from_vec(svec![ "deno", "test", "--coverage=cov", "--coverage-threshold=100" ]) .is_ok() ); } #[test] fn tier2_bool_validation() { let bad = [ svec!["deno", "fmt", "--use-tabs=maybe", "x.ts"], svec!["deno", "fmt", "--single-quote=maybe", "x.ts"], svec!["deno", "run", "--vendor=maybe", "x.ts"], svec!["deno", "run", "--frozen=maybe", "x.ts"], ]; for args in bad { assert!( flags_from_vec(args.clone()).is_err(), "expected error for {args:?}" ); } // Bare boolean-ish flags (no value) remain valid. assert!(flags_from_vec(svec!["deno", "fmt", "--use-tabs", "x.ts"]).is_ok()); } #[test] fn tier2_location_strips_credentials() { let flags = flags_from_vec(svec![ "deno", "run", "--location", "https://u:p@example.com/", "x.ts" ]) .unwrap(); let loc = flags.location.unwrap(); assert_eq!(loc.username(), ""); assert_eq!(loc.password(), None); assert_eq!(loc.host_str(), Some("example.com")); } // --------------------------------------------------------------------------- // Tier-3 parity: conflicts, requires, and aliases that clap enforced/accepted. // --------------------------------------------------------------------------- #[test] fn tier3_aliases() { // These previously-missing aliases/short flags now parse. let ok = [ svec!["deno", "run", "--importmap=im.json", "x.ts"], svec!["deno", "run", "--unsafe-proto", "x.ts"], svec!["deno", "install", "--production", "pkg"], svec!["deno", "ci", "--production"], svec!["deno", "fmt", "--options-use-tabs", "x.ts"], svec!["deno", "jupyter", "-d", "deno", "--install"], svec!["deno", "pack", "-o", "out", "x.ts"], ]; for args in ok { assert!( flags_from_vec(args.clone()).is_ok(), "expected ok for {args:?}" ); } } #[test] fn tier3_conflicts() { let bad = [ svec!["deno", "doc", "--html", "--json", "x.ts"], svec!["deno", "doc", "--filter=x", "--json", "x.ts"], svec!["deno", "run", "--coverage=c", "--inspect", "x.ts"], svec!["deno", "test", "--watch", "--no-run"], svec!["deno", "lint", "--rules-include=x", "--rules"], svec!["deno", "outdated", "--latest", "--compatible"], svec!["deno", "transpile", "--output=o", "--outdir=d", "x.ts"], svec!["deno", "x", "--install-alias=dx", "script.ts"], svec!["deno", "install", "--npm", "--jsr", "pkg"], svec!["deno", "install", "--global", "--dev", "pkg"], svec!["deno", "bump-version", "-c", "cfg", "--workspace"], ]; for args in bad { assert!( flags_from_vec(args.clone()).is_err(), "expected conflict error for {args:?}" ); } } #[test] fn tier3_requires() { let bad = [ // fmt --fail-fast requires --check svec!["deno", "fmt", "--fail-fast", "x.ts"], // doc html-only decorations require --html svec!["deno", "doc", "--category-docs=c", "x.ts"], // outdated --interactive requires --update svec!["deno", "outdated", "--interactive"], // coverage --output requires --lcov svec!["deno", "coverage", "--output=o", "cov"], // install --name requires --global svec!["deno", "install", "--name", "foo", "pkg"], ]; for args in bad { assert!( flags_from_vec(args.clone()).is_err(), "expected requires error for {args:?}" ); } // The dependency satisfied → ok. assert!( flags_from_vec(svec!["deno", "fmt", "--fail-fast", "--check", "x.ts"]) .is_ok() ); assert!( flags_from_vec(svec![ "deno", "install", "--global", "--name", "foo", "pkg" ]) .is_ok() ); } // --------------------------------------------------------------------------- // PR5 cutover: link / unlink subcommands (newly wired into the parser). // --------------------------------------------------------------------------- #[test] fn link_subcommand() { let flags = flags_from_vec(svec!["deno", "link", "../a", "../b", "--lockfile-only"]) .unwrap(); assert_eq!( flags.subcommand, DenoSubcommand::Link(LinkFlags { paths: svec!["../a", "../b"], lockfile_only: true, }) ); // lock args flow through to the top-level flags. let flags = flags_from_vec(svec!["deno", "link", "../a", "--frozen"]).unwrap(); assert_eq!(flags.frozen_lockfile, Some(true)); assert!(matches!( flags.subcommand, DenoSubcommand::Link(LinkFlags { lockfile_only: false, .. }) )); // at least one path is required. assert!(flags_from_vec(svec!["deno", "link"]).is_err()); } #[test] fn unlink_subcommand() { let flags = flags_from_vec(svec!["deno", "unlink", "@scope/name", "../c"]).unwrap(); assert_eq!( flags.subcommand, DenoSubcommand::Unlink(UnlinkFlags { names_or_paths: svec!["@scope/name", "../c"], lockfile_only: false, }) ); assert!(flags_from_vec(svec!["deno", "unlink"]).is_err()); } #[test] fn eval_double_dash_stripped() { // Unlike run/task, `deno eval` strips the `--` separator from argv // (clap plain trailing-var-arg, not `.last(true)`). let flags = flags_from_vec(svec!["deno", "eval", "console.log(1)", "--", "a&b"]) .unwrap(); assert_eq!(flags.argv, svec!["a&b"]); // run keeps it, for contrast. let flags = flags_from_vec(svec!["deno", "run", "x.ts", "--", "a&b"]).unwrap(); assert_eq!(flags.argv, svec!["--", "a&b"]); } #[test] fn sync_types_subcommand() { let flags = flags_from_vec(svec!["deno", "sync-types"]).unwrap(); assert_eq!( flags.subcommand, DenoSubcommand::SyncTypes(SyncTypesFlags::default()) ); let flags = flags_from_vec(svec!["deno", "sync-types", "a.ts", "tools/"]).unwrap(); assert_eq!( flags.subcommand, DenoSubcommand::SyncTypes(SyncTypesFlags { roots: svec!["a.ts", "tools/"], }) ); // --allow-import flows into permissions. let flags = flags_from_vec(svec!["deno", "sync-types", "--allow-import=example.com"]) .unwrap(); assert_eq!(flags.permissions.allow_import, Some(svec!["example.com"])); } #[test] fn serve_compile_keep_double_dash() { // serve/compile use clap's trailing_var_arg (like run), so they keep `--`. let flags = flags_from_vec(svec!["deno", "serve", "x.ts", "--", "-a"]).unwrap(); assert_eq!(flags.argv, svec!["--", "-a"]); let flags = flags_from_vec(svec!["deno", "compile", "x.ts", "--", "-a"]).unwrap(); assert!( matches!(flags.subcommand, DenoSubcommand::Compile(c) if c.args == svec!["--", "-a"]) ); } #[test] fn upgrade_no_delta() { let r = flags_from_vec(svec!["deno", "upgrade", "--no-delta"]); assert_eq!( r.unwrap(), Flags { subcommand: DenoSubcommand::Upgrade(UpgradeFlags { force: false, dry_run: false, canary: false, no_delta: true, release_candidate: false, version: None, output: None, version_or_hash_or_channel: None, checksum: None, pr: None, branch: None, }), ..Flags::default() } ); } #[test] fn hrtime_flags_are_accepted_as_no_ops() { // Removed in Deno 2, but still parsed so we can warn instead of erroring. for flag in ["--allow-hrtime", "--deny-hrtime"] { let r = flags_from_vec(svec!["deno", "run", flag, "script.ts"]); let flags = r.unwrap(); assert_eq!( flags.subcommand, DenoSubcommand::Run(RunFlags::new_default("script.ts".to_string())) ); } } #[test] fn use_env_proxy_flags() { let r = flags_from_vec(svec!["deno", "run", "--use-env-proxy", "script.ts"]); assert!(r.is_ok()); let r = flags_from_vec(svec!["deno", "run", "--no-use-env-proxy", "script.ts"]); assert!(r.is_ok()); let r = flags_from_vec(svec![ "deno", "run", "--use-env-proxy", "--no-use-env-proxy", "script.ts" ]); assert!(r.is_err()); }