use std::io::Write; use std::process::{Command, Output, Stdio}; fn avril(args: &[&str]) -> Output { let dir = tempfile::tempdir().unwrap(); std::fs::write(dir.path().join("main.js"), "console.log('ran');").unwrap(); Command::new(env!("CARGO_BIN_EXE_avril")) .current_dir(dir.path()) .args(args) .output() .unwrap() } fn stdout(args: &[&str]) -> String { let output = avril(args); assert!( output.status.success(), "{args:?}: {}", String::from_utf8_lossy(&output.stderr) ); String::from_utf8(output.stdout).unwrap() } fn stderr(args: &[&str]) -> String { let output = avril(args); assert!(!output.status.success(), "{args:?}"); assert!(output.stdout.is_empty(), "{args:?}"); String::from_utf8(output.stderr).unwrap() } #[test] fn global_help_lists_commands_and_command_help_lists_only_their_options() { for args in [&["--help"][..], &["-h"], &["help"], &["-0", "--help"]] { let help = stdout(args); for (heading, expected) in [ ( "Main commands:\n", &["repl", "run", "serve", "task", "test"][..], ), ( "Additional commands:\n", &[ "add", "bundle", "cache", "check", "info", "lsp", "update", "vendor", ][..], ), ] { let commands: Vec<_> = help .split_once(heading) .unwrap() .1 .lines() .take_while(|line| !line.is_empty()) .map(|line| line.split_whitespace().next().unwrap()) .collect(); assert_eq!(commands, expected, "{args:?}: {help}"); } assert!( help.find("Main commands:").unwrap() < help.find("Additional commands:").unwrap(), "{args:?}: {help}" ); assert!(!help.contains("--prune"), "{help}"); } for args in [ &["help", "update"][..], &["update", "--help"], &["update", "-h", "main.js"], ] { let help = stdout(args); assert!(help.contains("Usage: avril update"), "{help}"); assert!(help.contains("--prune"), "{help}"); assert!( !help.contains("--frozen") && !help.contains("--config"), "{help}" ); } let help = stdout(&["help", "task"]); assert!( help.contains("Usage: avril task [--watch] [ [args...]]"), "{help}" ); assert!(help.contains("--watch"), "{help}"); assert!(stderr(&["task", "--bogus"]).contains("Usage: avril task")); let help = stdout(&["help", "add"]); assert!(help.contains("\nAdd:\n --config=PATH"), "{help}"); assert!(help.contains("\nPackages:\n --offline"), "{help}"); assert!( !help.contains("--allow-") && !help.contains("--lock"), "{help}" ); assert!(stderr(&["help", "nope"]).contains("Unknown command: nope")); assert!(stderr(&["editor", "main.js"]).contains("Unknown command: editor")); assert!(stderr(&["help", "editor"]).contains("Unknown command: editor")); } #[test] fn package_commands_expose_explicit_registry_options() { for command in [ "run", "repl", "test", "serve", "cache", "info", "update", "vendor", "lsp", "add", ] { let help = stdout(&[command, "--help"]); assert!(help.contains("--jsr-registry=URL"), "{command}: {help}"); assert!(help.contains("--npm-registry=URL"), "{command}: {help}"); assert!(!help.contains("--registry"), "{command}: {help}"); assert!( stderr(&[command, "--registry=https://jsr.io/"]).contains("Unknown option: --registry"), "{command}" ); } } #[test] fn lsp_exposes_live_project_policy_and_rejects_retired_options() { for args in [&["lsp", "--help"][..], &["help", "lsp"]] { let help = stdout(args); assert!(!help.contains("--experimental-project")); assert!(help.contains("--typescript=PATH")); assert!(help.contains("--project=DIR") && help.contains("--lock")); } assert!(stderr(&["lsp"]).contains("requires --typescript")); assert!( stderr(&[ "lsp", "--typescript=missing", "--project=.", "--experimental-project=missing" ]) .contains("Unknown option") ); assert!(stderr(&["lsp", "--allow-all"]).contains("Unknown option")); assert!(stderr(&["lsp", "--typescript=a", "--typescript=b"]).contains("Duplicate option")); } #[test] fn check_exposes_a_typescript_backend_and_requires_it() { for args in [&["check", "--help"][..], &["help", "check"]] { let help = stdout(args); assert!( help.contains("Usage: avril check --typescript=PATH"), "{help}" ); assert!(help.contains("--typescript=PATH"), "{help}"); assert!(help.contains("--jsr-registry=URL"), "{help}"); assert!(help.contains("--lock=PATH"), "{help}"); } assert!(stderr(&["check", "main.js"]).contains("check requires --typescript")); assert!(stderr(&["check"]).contains("check requires --typescript")); assert!(stderr(&["check", "--typescript=backend"]).contains("Usage: avril check")); assert!( stderr(&["check", "--typescript=a", "--typescript=b", "main.js"]) .contains("Duplicate option") ); assert!( stderr(&["check", "--project=.", "main.js"]) .contains("--project is not an option of `avril check`") ); assert!( stderr(&["run", "--typescript=backend", "main.js"]) .contains("--typescript is not an option of `avril run`") ); } #[test] fn commands_reject_options_they_do_not_take() { for (args, message) in [ ( &["run", "--pre", "main.js"][..], "--pre is not an option of `avril run`", ), (&["main.js"], "Unknown command: main.js"), (&["-0", "main.js"], "The REPL does not accept a module"), (&["--prune"], "--prune is not an option of `avril repl`"), ( &["repl", "--include=x"], "--include is not an option of `avril repl`", ), ( &["update", "--frozen", "main.js"], "--frozen is not an option of `avril update`", ), ( &["cache", "--out=x", "main.js"], "--out is not an option of `avril cache`", ), ( &["add", "--lock=x", "jsr:@test/pkg@1"], "--lock is not an option of `avril add`", ), ( &["add", "-0", "jsr:@test/pkg@1"], "-0 is not an option of `avril add`", ), (&["run", "--bogus", "main.js"], "Unknown option: --bogus"), ( &["run", "--offline=yes", "main.js"], "--offline does not take a value", ), ( &["add", "--pre=yes", "jsr:@test/pkg"], "--pre does not take a value", ), (&["cache"], "Usage: avril cache [OPTIONS] "), ] { let error = stderr(args); assert!(error.contains(message), "{args:?}: {error}"); } assert_eq!(stdout(&["run", "--", "main.js"]), "ran\n"); } #[test] fn without_a_command_avril_only_starts_the_repl() { for args in [&[][..], &["-0"]] { let mut child = Command::new(env!("CARGO_BIN_EXE_avril")) .args(args) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .unwrap(); child.stdin.take().unwrap().write_all(b"6 * 7\n").unwrap(); let output = child.wait_with_output().unwrap(); assert!(output.status.success(), "{args:?}"); assert_eq!(output.stdout, b"42\n", "{args:?}"); } }