use nu_test_support::fs::Stub::EmptyFile; use nu_test_support::nu; use nu_test_support::playground::Playground; use pretty_assertions::assert_eq; #[test] fn shows_error_for_command_not_found() { let actual = nu!("ferris_is_not_here.exe"); assert!(!actual.err.is_empty()); } #[test] fn shows_error_for_command_not_found_in_pipeline() { let actual = nu!("ferris_is_not_here.exe | echo done"); assert!(!actual.err.is_empty()); } #[ignore] // jt: we can't test this using the -c workaround currently #[test] fn automatically_change_directory() { use nu_test_support::playground::Playground; Playground::setup("cd_test_5_1", |dirs, sandbox| { sandbox.mkdir("autodir"); let actual = nu!( cwd: dirs.test(), " autodir echo (pwd) " ); assert!(actual.out.ends_with("autodir")); }) } // FIXME: jt: we don't currently support autocd in testing #[ignore] #[test] fn automatically_change_directory_with_trailing_slash_and_same_name_as_command() { use nu_test_support::playground::Playground; Playground::setup("cd_test_5_1", |dirs, sandbox| { sandbox.mkdir("cd"); let actual = nu!( cwd: dirs.test(), " cd/ pwd " ); assert!(actual.out.ends_with("cd")); }) } #[test] fn pass_dot_as_external_arguments() { let actual = nu!("nu --testbin cococo ."); assert_eq!(actual.out, "."); } #[test] fn correctly_escape_external_arguments() { let actual = nu!("^nu --testbin cococo '$0'"); assert_eq!(actual.out, "$0"); } #[test] fn escape_also_escapes_equals() { let actual = nu!("^MYFOONAME=MYBARVALUE"); assert!(actual .err .contains("Command `MYFOONAME=MYBARVALUE` not found")); } #[test] fn execute_binary_in_string() { let actual = nu!(r#" let cmd = "nu" ^$"($cmd)" --testbin cococo "$0" "#); assert_eq!(actual.out, "$0"); } #[test] fn single_quote_dollar_external() { let actual = nu!("let author = 'JT'; nu --testbin cococo $'foo=($author)'"); assert_eq!(actual.out, "foo=JT"); } #[test] fn redirects_custom_command_external() { let actual = nu!("def foo [] { nu --testbin cococo foo bar }; foo | str length"); assert_eq!(actual.out, "7"); } #[test] fn passes_binary_data_between_externals() { let actual = nu!(cwd: "tests/fixtures/formats", "nu --testbin meowb sample.db | nu --testbin relay | hash sha256"); assert_eq!( actual.out, "2f5050e7eea415c1f3d80b5d93355efd15043ec9157a2bb167a9e73f2ae651f2" ) } #[test] fn command_not_found_error_suggests_search_term() { // 'distinct' is not a command, but it is a search term for 'uniq' let actual = nu!("ls | distinct"); assert!(actual.err.contains("uniq")); } #[test] fn command_not_found_error_suggests_typo_fix() { let actual = nu!("benchmark { echo 'foo'}"); assert!(actual.err.contains("timeit")); } #[cfg(not(windows))] #[test] fn command_not_found_error_recognizes_non_executable_file() { let actual = nu!("./Cargo.toml"); assert!(actual.err.contains( "refers to a file that is not executable. Did you forget to set execute permissions?" )); } #[test] fn command_not_found_error_shows_not_found_1() { let actual = nu!(r#" export extern "foo" []; foo "#); assert!(actual.err.contains("Command `foo` not found")); } #[test] fn command_substitution_wont_output_extra_newline() { let actual = nu!(r#" with-env { FOO: "bar" } { echo $"prefix (nu --testbin echo_env FOO) suffix" } "#); assert_eq!(actual.out, "prefix bar suffix"); let actual = nu!(r#" with-env { FOO: "bar" } { (nu --testbin echo_env FOO) } "#); assert_eq!(actual.out, "bar"); } #[rstest::rstest] #[case("err>|")] #[case("e>|")] fn basic_err_pipe_works(#[case] redirection: &str) { let actual = nu!( r#"with-env { FOO: "bar" } { nu --testbin echo_env_stderr FOO {redirection} str length }"# .replace("{redirection}", redirection) ); assert_eq!(actual.out, "3"); } #[rstest::rstest] #[case("out+err>|")] #[case("err+out>|")] #[case("o+e>|")] #[case("e+o>|")] fn basic_outerr_pipe_works(#[case] redirection: &str) { let actual = nu!( r#"with-env { FOO: "bar" } { nu --testbin echo_env_mixed out-err FOO FOO {redirection} str length }"# .replace("{redirection}", redirection) ); assert_eq!(actual.out, "7"); } #[test] fn dont_run_glob_if_pass_variable_to_external() { Playground::setup("dont_run_glob", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("jt_likes_cake.txt"), EmptyFile("andres_likes_arepas.txt"), ]); let actual = nu!(cwd: dirs.test(), r#"let f = "*.txt"; nu --testbin nonu $f"#); assert_eq!(actual.out, "*.txt"); }) } #[test] fn run_glob_if_pass_variable_to_external() { Playground::setup("run_glob_on_external", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("jt_likes_cake.txt"), EmptyFile("andres_likes_arepas.txt"), ]); let actual = nu!(cwd: dirs.test(), r#"let f = "*.txt"; nu --testbin nonu ...(glob $f)"#); assert!(actual.out.contains("jt_likes_cake.txt")); assert!(actual.out.contains("andres_likes_arepas.txt")); }) } #[test] fn subexpression_does_not_implicitly_capture() { let actual = nu!("(nu --testbin cococo); null"); assert_eq!(actual.out, "cococo"); } mod it_evaluation { use super::nu; use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed}; use nu_test_support::{pipeline, playground::Playground}; #[test] fn takes_rows_of_nu_value_strings() { Playground::setup("it_argument_test_1", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("jt_likes_cake.txt"), EmptyFile("andres_likes_arepas.txt"), ]); let actual = nu!( cwd: dirs.test(), pipeline( " ls | sort-by name | get name | each { |it| nu --testbin cococo $it } | get 1 " )); assert_eq!(actual.out, "jt_likes_cake.txt"); }) } #[test] fn takes_rows_of_nu_value_lines() { Playground::setup("it_argument_test_2", |dirs, sandbox| { sandbox.with_files(&[FileWithContentToBeTrimmed( "nu_candies.txt", " AndrásWithKitKatzz AndrásWithKitKatz ", )]); let actual = nu!( cwd: dirs.test(), pipeline( " open nu_candies.txt | lines | each { |it| nu --testbin chop $it} | get 1 " )); assert_eq!(actual.out, "AndrásWithKitKat"); }) } #[test] fn can_properly_buffer_lines_externally() { let actual = nu!(" nu --testbin repeater c 8197 | lines | length "); assert_eq!(actual.out, "1"); } #[test] fn supports_fetching_given_a_column_path_to_it() { Playground::setup("it_argument_test_3", |dirs, sandbox| { sandbox.with_files(&[FileWithContent( "sample.toml", r#" nu_party_venue = "zion" "#, )]); let actual = nu!( cwd: dirs.test(), pipeline( " open sample.toml | nu --testbin cococo $in.nu_party_venue " )); assert_eq!(actual.out, "zion"); }) } } mod stdin_evaluation { use super::nu; use nu_test_support::pipeline; #[test] fn does_not_panic_with_no_newline_in_stream() { let actual = nu!(pipeline( r#" nu --testbin nonu "where's the nuline?" | length "# )); assert_eq!(actual.err, ""); } #[test] fn does_not_block_indefinitely() { let stdout = nu!(pipeline( " ( nu --testbin iecho yes | nu --testbin chop | nu --testbin chop | lines | first ) " )) .out; assert_eq!(stdout, "y"); } } mod external_words { use super::nu; use nu_test_support::fs::Stub::FileWithContent; use nu_test_support::{pipeline, playground::Playground}; #[test] fn relaxed_external_words() { let actual = nu!(" nu --testbin cococo joturner@foo.bar.baz "); assert_eq!(actual.out, "joturner@foo.bar.baz"); } #[test] fn raw_string_as_external_argument() { let actual = nu!("nu --testbin cococo r#'asdf'#"); assert_eq!(actual.out, "asdf"); } //FIXME: jt: limitation in testing - can't use single ticks currently #[ignore] #[test] fn no_escaping_for_single_quoted_strings() { let actual = nu!(r#" nu --testbin cococo 'test "things"' "#); assert_eq!(actual.out, "test \"things\""); } #[rstest::rstest] #[case("sample.toml", r#""sample.toml""#)] #[case("a sample file.toml", r#""a sample file.toml""#)] //FIXME: jt: we don't currently support single ticks in tests //#[case("quote'mark.toml", r#""quote'mark.toml""#)] #[cfg_attr( not(target_os = "windows"), case(r#"quote"mark.toml"#, r#"$"quote(char double_quote)mark.toml""#) )] #[cfg_attr(not(target_os = "windows"), case("?mark.toml", r#""?mark.toml""#))] #[cfg_attr(not(target_os = "windows"), case("*.toml", r#""*.toml""#))] #[cfg_attr(not(target_os = "windows"), case("*.toml", "*.toml"))] #[case("$ sign.toml", r#""$ sign.toml""#)] fn external_arg_with_special_characters(#[case] path: &str, #[case] nu_path_argument: &str) { Playground::setup("external_arg_with_quotes", |dirs, sandbox| { sandbox.with_files(&[FileWithContent( path, r#" nu_party_venue = "zion" "#, )]); let actual = nu!( cwd: dirs.test(), pipeline( &format!(" nu --testbin meow {nu_path_argument} | from toml | get nu_party_venue ") )); assert_eq!(actual.out, "zion"); }) } } mod nu_commands { use nu_test_support::playground::Playground; use super::nu; #[test] fn echo_internally_externally() { let actual = nu!(r#" nu -n -c "echo 'foo'" "#); assert_eq!(actual.out, "foo"); } #[test] fn failed_with_proper_exit_code() { Playground::setup("external failed", |dirs, _sandbox| { let actual = nu!(cwd: dirs.test(), r#" nu -n -c "cargo build | complete | get exit_code" "#); // cargo for non rust project's exit code is 101. assert_eq!(actual.out, "101") }) } #[test] fn better_arg_quoting() { let actual = nu!(r#" nu -n -c "\# '" "#); assert_eq!(actual.out, ""); } #[test] fn command_list_arg_test() { let actual = nu!(" nu ...['-n' '-c' 'version'] "); assert!(actual.out.contains("version")); assert!(actual.out.contains("rust_version")); assert!(actual.out.contains("rust_channel")); } #[test] fn command_cell_path_arg_test() { let actual = nu!(" nu ...([ '-n' '-c' 'version' ]) "); assert!(actual.out.contains("version")); assert!(actual.out.contains("rust_version")); assert!(actual.out.contains("rust_channel")); } } mod nu_script { use super::nu; #[test] fn run_nu_script() { let actual = nu!(cwd: "tests/fixtures/formats", " nu -n script.nu "); assert_eq!(actual.out, "done"); } #[test] fn run_nu_script_multiline() { let actual = nu!(cwd: "tests/fixtures/formats", " nu -n script_multiline.nu "); assert_eq!(actual.out, "23"); } } mod tilde_expansion { use super::nu; #[test] fn as_home_directory_when_passed_as_argument_and_begins_with_tilde() { let actual = nu!(" nu --testbin cococo ~ "); assert!(!actual.out.contains('~')); } #[test] fn does_not_expand_when_passed_as_argument_and_does_not_start_with_tilde() { let actual = nu!(r#" nu --testbin cococo "1~1" "#); assert_eq!(actual.out, "1~1"); } } mod external_command_arguments { use super::nu; use nu_test_support::fs::Stub::EmptyFile; use nu_test_support::{pipeline, playground::Playground}; #[test] fn expands_table_of_primitives_to_positional_arguments() { Playground::setup( "expands_table_of_primitives_to_positional_arguments", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("jt_likes_cake.txt"), EmptyFile("andres_likes_arepas.txt"), EmptyFile("ferris_not_here.txt"), ]); let actual = nu!( cwd: dirs.test(), pipeline( " nu --testbin cococo ...(ls | get name) " )); assert_eq!( actual.out, "andres_likes_arepas.txt ferris_not_here.txt jt_likes_cake.txt" ); }, ) } #[test] fn proper_subexpression_paths_in_external_args() { Playground::setup( "expands_table_of_primitives_to_positional_arguments", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("jt_likes_cake.txt"), EmptyFile("andres_likes_arepas.txt"), EmptyFile("ferris_not_here.txt"), ]); let actual = nu!( cwd: dirs.test(), pipeline( " nu --testbin cococo (ls | sort-by name | get name).1 " )); assert_eq!(actual.out, "ferris_not_here.txt"); }, ) } #[cfg(not(windows))] #[test] fn string_interpolation_with_an_external_command() { Playground::setup( "string_interpolation_with_an_external_command", |dirs, sandbox| { sandbox.mkdir("cd"); sandbox.with_files(&[EmptyFile("cd/jt_likes_cake.txt")]); let actual = nu!( cwd: dirs.test(), pipeline( r#" nu --testbin cococo $"(pwd)/cd" "# )); assert!(actual.out.contains("cd")); }, ) } #[test] fn semicolons_are_sanitized_before_passing_to_subshell() { let actual = nu!("nu --testbin cococo \"a;b\""); assert_eq!(actual.out, "a;b"); } #[test] fn ampersands_are_sanitized_before_passing_to_subshell() { let actual = nu!("nu --testbin cococo \"a&b\""); assert_eq!(actual.out, "a&b"); } #[cfg(not(windows))] #[test] fn subcommands_are_sanitized_before_passing_to_subshell() { let actual = nu!("nu --testbin cococo \"$(ls)\""); assert_eq!(actual.out, "$(ls)"); } #[cfg(not(windows))] #[test] fn shell_arguments_are_sanitized_even_if_coming_from_other_commands() { let actual = nu!("nu --testbin cococo (echo \"a;&$(hello)\")"); assert_eq!(actual.out, "a;&$(hello)"); } #[test] fn remove_quotes_in_shell_arguments() { let actual = nu!("nu --testbin cococo expression='-r -w'"); assert_eq!(actual.out, "expression=-r -w"); let actual = nu!(r#"nu --testbin cococo expression="-r -w""#); assert_eq!(actual.out, "expression=-r -w"); let actual = nu!("nu --testbin cococo expression='-r -w'"); assert_eq!(actual.out, "expression=-r -w"); let actual = nu!(r#"nu --testbin cococo expression="-r\" -w""#); assert_eq!(actual.out, r#"expression=-r" -w"#); let actual = nu!(r#"nu --testbin cococo expression='-r\" -w'"#); assert_eq!(actual.out, r#"expression=-r\" -w"#); } } #[test] fn exit_code_stops_execution_closure() { let actual = nu!("[1 2] | each {|x| nu -c $'exit ($x)'; print $x }"); assert!(actual.out.is_empty()); assert!(actual.err.contains("exited with code 1")); } // TODO: need to add tests under display_error.exit_code = true #[test] fn exit_code_stops_execution_custom_command() { let actual = nu!("def cmd [] { nu -c 'exit 42'; 'ok1' }; cmd; print 'ok2'"); assert!(actual.out.is_empty()); assert!(!actual.err.contains("exited with code 42")); } // TODO: need to add tests under display_error.exit_code = true #[test] fn exit_code_stops_execution_for_loop() { let actual = nu!("for x in [0 1] { nu -c 'exit 42'; print $x }"); assert!(actual.out.is_empty()); assert!(!actual.err.contains("exited with code 42")); } #[test] fn arg_dont_run_subcommand_if_surrounded_with_quote() { let actual = nu!("nu --testbin cococo `(echo aa)`"); assert_eq!(actual.out, "(echo aa)"); let actual = nu!("nu --testbin cococo \"(echo aa)\""); assert_eq!(actual.out, "(echo aa)"); let actual = nu!("nu --testbin cococo '(echo aa)'"); assert_eq!(actual.out, "(echo aa)"); }