ralph-claude-code/tests/unit/test_wizard_utils.bats
Frank Bria 05c57207f3
fix(wizard): redirect prompts to stderr for clean command substitution (#130)
When wizard prompt functions (prompt_text, prompt_number, select_option,
select_with_default) were used with command substitution, ANSI-colored
prompts were captured along with user responses, corrupting .ralphrc
values like: PROJECT_NAME="[0;36mProject name[0m [value]: value"

Fix:
- Redirect all display output (colored prompts, validation messages) to
  stderr using >&2, matching the pattern already used by select_multiple()
- Keep only the actual response/result on stdout for command substitution

Changes:
- lib/wizard_utils.sh: Add >&2 to prompt_text (lines 84-87),
  prompt_number (lines 118-121, 131-151), confirm (lines 44, 60),
  select_option (lines 190-215), select_with_default (lines 330-364)
- tests/unit/test_wizard_utils.bats: Add 20 new tests for stdout/stderr
  separation and clean command substitution results
- CLAUDE.md: Update test count to 420

Test count: 420 (up from 396)

Co-authored-by: Test User <test@example.com>
2026-01-26 14:47:06 -07:00

206 lines
6.9 KiB
Bash

#!/usr/bin/env bats
# Unit tests for wizard_utils.sh
# Tests for stdout/stderr separation to prevent ANSI code capture in command substitution
load '../helpers/test_helper'
# Path to the library
WIZARD_UTILS="${BATS_TEST_DIRNAME}/../../lib/wizard_utils.sh"
setup() {
# Source the wizard utils
source "$WIZARD_UTILS"
}
# =============================================================================
# PROMPT_TEXT STDOUT/STDERR SEPARATION (4 tests)
# =============================================================================
@test "prompt_text returns only user input on stdout, no ANSI codes" {
# Simulate user typing "my-project" followed by Enter
result=$(echo "my-project" | prompt_text "Project name" "default")
# The captured output should be ONLY "my-project", no ANSI escape codes
[[ "$result" == "my-project" ]]
# Should NOT contain ANSI escape sequence
[[ ! "$result" =~ $'\033' ]]
[[ ! "$result" =~ "Project name" ]]
}
@test "prompt_text with default returns clean default value on empty input" {
# Simulate user pressing Enter (empty input)
result=$(echo "" | prompt_text "Project name" "default-value")
# Should return only the default value
[[ "$result" == "default-value" ]]
# Should NOT contain ANSI escape sequence
[[ ! "$result" =~ $'\033' ]]
[[ ! "$result" =~ "Project name" ]]
}
@test "prompt_text without default returns empty string on empty input" {
# Simulate user pressing Enter (empty input) with no default
result=$(echo "" | prompt_text "Project name")
# Should return empty string
[[ "$result" == "" ]]
}
@test "prompt_text handles special characters in user input" {
# Test that special characters are preserved
result=$(echo "my project-name_123" | prompt_text "Name" "default")
[[ "$result" == "my project-name_123" ]]
}
# =============================================================================
# PROMPT_NUMBER STDOUT/STDERR SEPARATION (5 tests)
# =============================================================================
@test "prompt_number returns only numeric value on stdout, no ANSI codes" {
# Simulate user typing "100"
result=$(echo "100" | prompt_number "Max calls" "50")
# The captured output should be ONLY "100", no ANSI codes
[[ "$result" == "100" ]]
# Should NOT contain ANSI escape sequence
[[ ! "$result" =~ $'\033' ]]
[[ ! "$result" =~ "Max calls" ]]
}
@test "prompt_number with default returns clean default on empty input" {
# Simulate user pressing Enter (empty input)
result=$(echo "" | prompt_number "Max calls" "50")
# Should return only the default
[[ "$result" == "50" ]]
# Should NOT contain ANSI escape sequence
[[ ! "$result" =~ $'\033' ]]
}
@test "prompt_number validates min value without ANSI in result" {
# First input is too small, second input is valid
result=$(printf "5\n20" | prompt_number "Value" "" "10" "100")
# Should return only the valid number
[[ "$result" == "20" ]]
[[ ! "$result" =~ $'\033' ]]
}
@test "prompt_number validates max value without ANSI in result" {
# First input is too large, second input is valid
result=$(printf "150\n80" | prompt_number "Value" "" "10" "100")
# Should return only the valid number
[[ "$result" == "80" ]]
[[ ! "$result" =~ $'\033' ]]
}
@test "prompt_number rejects non-numeric input without ANSI in result" {
# First input is not a number, second input is valid
result=$(printf "abc\n42" | prompt_number "Value" "")
# Should return only the valid number
[[ "$result" == "42" ]]
[[ ! "$result" =~ $'\033' ]]
}
# =============================================================================
# CONFIRM STDOUT/STDERR SEPARATION (3 tests)
# =============================================================================
@test "confirm sends prompt to stderr not stdout" {
# Capture stdout only - should be empty
stdout_output=$(echo "y" | confirm "Continue?" 2>/dev/null)
# Stdout should be empty (confirm uses return codes, not echo)
[[ -z "$stdout_output" ]]
}
@test "confirm yes response returns 0" {
echo "y" | confirm "Continue?" 2>/dev/null
[[ $? -eq 0 ]]
}
@test "confirm no response returns 1" {
# Run confirm with "n" response, expect return code 1 (no)
run bash -c 'source '"$WIZARD_UTILS"' && echo "n" | confirm "Continue?" 2>/dev/null'
[[ $status -eq 1 ]]
}
# =============================================================================
# SELECT_OPTION STDOUT/STDERR SEPARATION (3 tests)
# =============================================================================
@test "select_option returns only selected option on stdout, no ANSI codes" {
# Simulate user selecting option 2
result=$(echo "2" | select_option "Choose one" "first" "second" "third")
# Should return only "second"
[[ "$result" == "second" ]]
[[ ! "$result" =~ $'\033' ]]
[[ ! "$result" =~ "Choose one" ]]
}
@test "select_option handles first option selection" {
result=$(echo "1" | select_option "Choose" "alpha" "beta")
[[ "$result" == "alpha" ]]
[[ ! "$result" =~ $'\033' ]]
}
@test "select_option retries on invalid input without ANSI in result" {
# First input is invalid, second input is valid
result=$(printf "abc\n2" | select_option "Choose" "one" "two")
[[ "$result" == "two" ]]
[[ ! "$result" =~ $'\033' ]]
}
# =============================================================================
# SELECT_WITH_DEFAULT STDOUT/STDERR SEPARATION (3 tests)
# =============================================================================
@test "select_with_default returns selected option on stdout, no ANSI codes" {
# Simulate user selecting option 2
result=$(echo "2" | select_with_default "Choose" 1 "default" "other")
[[ "$result" == "other" ]]
[[ ! "$result" =~ $'\033' ]]
}
@test "select_with_default returns default on empty input" {
# Simulate user pressing Enter
result=$(echo "" | select_with_default "Choose" 1 "default" "other")
[[ "$result" == "default" ]]
[[ ! "$result" =~ $'\033' ]]
}
@test "select_with_default handles non-first default" {
# Simulate empty input with default_index=2
result=$(echo "" | select_with_default "Choose" 2 "first" "second")
[[ "$result" == "second" ]]
[[ ! "$result" =~ $'\033' ]]
}
# =============================================================================
# INTEGRATION: RALPHRC GENERATION (2 tests)
# =============================================================================
@test "prompt_text output is safe for shell variable assignment" {
result=$(echo "test-project" | prompt_text "Project" "default")
# Result should be safe to assign to a shell variable
eval "PROJECT_NAME=\"$result\""
[[ "$PROJECT_NAME" == "test-project" ]]
}
@test "prompt_number output is safe for shell arithmetic" {
result=$(echo "100" | prompt_number "Calls" "50")
# Result should be a valid number for arithmetic
(( result > 0 ))
[[ "$result" -eq 100 ]]
}