diff --git a/languages/haskell/runnables.scm b/languages/haskell/runnables.scm index 7b9959d..5cd97ff 100644 --- a/languages/haskell/runnables.scm +++ b/languages/haskell/runnables.scm @@ -9,3 +9,11 @@ (#eq? @run "main") (#set! tag haskell-build) (#set! tag haskell-run)) + +; Detect describe/it test blocks +((apply + function: (variable) @run + (#any-of? @run "describe" "it") + argument: (literal + (string) @HASKELL_TEST_NAME)) + (#set! tag haskell-test)) diff --git a/languages/haskell/tasks.json b/languages/haskell/tasks.json index 58d86e7..59e79dd 100644 --- a/languages/haskell/tasks.json +++ b/languages/haskell/tasks.json @@ -22,5 +22,17 @@ } }, "show_command": false + }, + { + "label": "Test $ZED_CUSTOM_HASKELL_TEST_NAME", + "command": "name=$(printf '%s' \"$ZED_CUSTOM_HASKELL_TEST_NAME\" | tr -d '\"'); if [ -f stack.yaml ]; then stack test --ta \"--match /$name\"; elif [ -f cabal.project ] || ls *.cabal 2>/dev/null | grep -q .; then cabal test --test-options \"--match /$name\"; else echo 'No Stack or Cabal found to run the tests' >&2; exit 1; fi", + "tags": ["haskell-test"], + "shell": { + "with_arguments": { + "program": "/bin/sh", + "args": ["-c"] + } + }, + "show_command": false } ] diff --git a/tests/task_verification_test.rs b/tests/task_verification_test.rs index c62815e..05facb7 100644 --- a/tests/task_verification_test.rs +++ b/tests/task_verification_test.rs @@ -88,6 +88,21 @@ impl TestProject { String::from_utf8_lossy(&output.stdout).into_owned() + &String::from_utf8_lossy(&output.stderr) } + + fn run_task_with_env(&self, tag: &str, env: &[(&str, &str)]) -> String { + let command = get_task_command_by_tag(tag); + let mut cmd = Command::new("sh"); + cmd.arg("-c") + .arg(&command) + .env("PATH", &self.new_path) + .current_dir(&self.temp_dir); + for (key, val) in env { + cmd.env(key, val); + } + let output = cmd.output().expect("Failed to execute shell command"); + String::from_utf8_lossy(&output.stdout).into_owned() + + &String::from_utf8_lossy(&output.stderr) + } } impl Drop for TestProject { @@ -167,3 +182,45 @@ fn test_no_build_tool() { output ); } + +// ZED_CUSTOM_HASKELL_TEST_NAME is the string token as captured by tree-sitter, +// which includes the surrounding quotes (e.g. `"Functor"`, not `Functor`). +// The task command strips them via `tr -d '"'` before passing to --match. + +#[test] +fn test_stack_test_strips_quotes_and_passes_match() { + let project = TestProject::new("stack_test_match"); + project.with_stack(); + let output = project.run_task_with_env( + "haskell-test", + &[("ZED_CUSTOM_HASKELL_TEST_NAME", "\"Functor\"")], + ); + assert!( + output.contains("STACK_CALLED: test --ta --match /Functor"), + "Got: {output}" + ); +} + +#[test] +fn test_cabal_test_strips_quotes_and_passes_match() { + let project = TestProject::new("cabal_test_match"); + project.with_cabal(); + let output = project.run_task_with_env( + "haskell-test", + &[("ZED_CUSTOM_HASKELL_TEST_NAME", "\"RemoteData\"")], + ); + assert!( + output.contains("CABAL_CALLED: test --test-options --match /RemoteData"), + "Got: {output}" + ); +} + +#[test] +fn test_no_build_tool_test() { + let project = TestProject::new("no_tool_test"); + let output = project.run_task_with_env( + "haskell-test", + &[("ZED_CUSTOM_HASKELL_TEST_NAME", "\"Functor\"")], + ); + assert!(output.contains("No Stack or Cabal found"), "Got: {output}"); +}