#!/bin/bash set -euo pipefail # ── Setup ───────────────────────────────────────────────────────────────────── # Using a static-ish path since we can't use command substitution in cat TEST_DIR="/tmp/color-tool-tests-repro" BIN_DIR="$TEST_DIR/bin" rm -rf "$TEST_DIR" mkdir -p "$BIN_DIR" # Mock notify-send cat <<'MOCK' > "$BIN_DIR/notify-send" #!/bin/bash echo "NOTIFY: $*" >> "/tmp/color-tool-tests-repro/notify.log" MOCK # Mock wl-copy cat <<'MOCK' > "$BIN_DIR/wl-copy" #!/bin/bash cat > "/tmp/color-tool-tests-repro/clipboard.txt" MOCK # Mock python3 cat <<'MOCK' > "$BIN_DIR/python3" #!/bin/bash if [[ "$*" == *wl-colorpicker*.py ]]; then echo "#aabbcc" exit 0 fi exec /usr/bin/python3 "$@" MOCK chmod +x "$BIN_DIR"/* # Symlink essential tools ln -s /usr/bin/bash "$BIN_DIR/bash" ln -s /usr/bin/cat "$BIN_DIR/cat" ln -s /usr/bin/printf "$BIN_DIR/printf" ln -s /usr/bin/echo "$BIN_DIR/echo" ln -s /usr/bin/sed "$BIN_DIR/sed" ln -s /usr/bin/grep "$BIN_DIR/grep" ln -s /usr/bin/awk "$BIN_DIR/awk" ln -s /usr/bin/jq "$BIN_DIR/jq" ln -s /usr/bin/readlink "$BIN_DIR/readlink" ln -s /usr/bin/dirname "$BIN_DIR/dirname" ln -s /usr/bin/mkdir "$BIN_DIR/mkdir" ln -s /usr/bin/mktemp "$BIN_DIR/mktemp" ln -s /usr/bin/chmod "$BIN_DIR/chmod" ln -s /usr/bin/rm "$BIN_DIR/rm" # Mock curl for thecolorapi.com cat <<'MOCK' > "$BIN_DIR/curl" #!/bin/bash if [[ "$*" == *"thecolorapi.com"* ]]; then echo '{"name": {"value": "Mocked Color Name"}}' exit 0 fi exec /usr/bin/curl "$@" MOCK chmod +x "$BIN_DIR/curl" ln -s /usr/bin/mv "$BIN_DIR/mv" ln -s /usr/bin/tr "$BIN_DIR/tr" ln -s /usr/bin/cp "$BIN_DIR/cp" ln -s /usr/bin/ln "$BIN_DIR/ln" export PATH="$BIN_DIR" export HOME="$TEST_DIR" export XDG_CONFIG_HOME="$TEST_DIR/.config" export XDG_DATA_HOME="$TEST_DIR/.local/share" export WAYLAND_DISPLAY=wayland-0 export XDG_CURRENT_DESKTOP=KDE # Resolve the absolute path of color-tool COLOR_TOOL="$(/usr/bin/readlink -f ./color-tool)" # ── Test Helpers ────────────────────────────────────────────────────────────── total=0 passed=0 it() { local label="$1" total=$((total + 1)) printf "Test: %s... " "$label" } assert_contains() { local haystack="$1" local needle="$2" if [[ "$haystack" == *"$needle"* ]]; then echo "PASS" passed=$((passed + 1)) else echo "FAIL" echo " Expected to find: $needle" echo " Actual output: $haystack" return 1 fi } # ── Tests ───────────────────────────────────────────────────────────────────── # 1. Basic Conversion it "converts hex to rgb" output=$("$COLOR_TOOL" "#ffffff" --output rgb --no-copy --no-notify) assert_contains "$output" "rgb(255, 255, 255)" # 2. Multiple Formats it "handles multiple formats" output=$("$COLOR_TOOL" "#000000" --output hex,rgba --no-copy --no-notify) assert_contains "$output" "#000000 rgba(0, 0, 0, 1.0)" # 3. Clipboard Integration it "copies to clipboard (mocked)" rm -f "$TEST_DIR/clipboard.txt" "$COLOR_TOOL" "#ff0000" --copy --no-notify >/dev/null if [[ -f "$TEST_DIR/clipboard.txt" ]]; then assert_contains "$(cat "$TEST_DIR/clipboard.txt")" "#ff0000" else echo "FAIL (clipboard.txt not created)" fi # 4. Notification Integration it "sends notifications (mocked)" rm -f "$TEST_DIR/notify.log" "$COLOR_TOOL" "#00ff00" --notify --no-copy >/dev/null if [[ -f "$TEST_DIR/notify.log" ]]; then assert_contains "$(cat "$TEST_DIR/notify.log")" "NOTIFY: -i color-picker color-tool #00ff00" else echo "FAIL (notify.log not created)" fi # 5. Color Picker (Mocked) it "launches color picker and processes result" output=$("$COLOR_TOOL" --pick --no-copy --no-notify) assert_contains "$output" "#aabbcc" # 6. Config Loading it "loads defaults from config.toml" mkdir -p "$HOME/.config/color-tool" cat <<'CONF' > "$HOME/.config/color-tool/config.toml" [defaults] output = "rgba" CONF output=$("$COLOR_TOOL" "#ffffff" --no-copy --no-notify) assert_contains "$output" "rgba(255, 255, 255, 1.0)" # 7. Error Notification it "notifies on missing clipboard utility" rm -f "$BIN_DIR/wl-copy" rm -f "$TEST_DIR/notify.log" "$COLOR_TOOL" "#123456" --copy --notify 2>/dev/null >/dev/null if [[ -f "$TEST_DIR/notify.log" ]]; then assert_contains "$(cat "$TEST_DIR/notify.log")" "NOTIFY: -u normal -i dialog-warning color-tool Missing clipboard utility" else echo "FAIL (notify.log not created)" fi # 8. Config Management it "manages configuration via CLI" mkdir -p "$HOME/.config/color-tool" "$COLOR_TOOL" --reset-config >/dev/null # Test --set-config "$COLOR_TOOL" --set-config defaults output=rgba output=$("$COLOR_TOOL" --get-config) if [[ "$output" == *"output = \"rgba\""* ]]; then # Test --reset-config "$COLOR_TOOL" --reset-config >/dev/null output=$("$COLOR_TOOL" --get-config) assert_contains "$output" "output = \"hex\"" else echo "FAIL" echo " Expected to find: output = \"rgba\"" echo " Actual output: $output" fi # 9. JSON Output it "outputs selected formats as JSON" output=$("$COLOR_TOOL" "#00ff00" --output hex,rgb --json --no-copy --no-notify) if [[ "$output" == *'"hex": "#00ff00"'* ]] && [[ "$output" == *'"rgb": "rgb(0, 255, 0)"'* ]]; then echo "PASS" passed=$((passed + 1)) else echo "FAIL" echo " Actual output: $output" fi # 10. Swatch Output it "outputs visual swatch" output=$("$COLOR_TOOL" "#112233" --output hex --swatch --no-copy --no-notify) if [[ "$output" == *"$(printf "\033[48;2;17;34;51m \033[0m")"* ]] && [[ "$output" == *"#112233"* ]]; then echo "PASS" passed=$((passed + 1)) else echo "FAIL" echo " Actual output: $output" fi # 11. Color Name Fetching it "fetches color name from thecolorapi.com" output=$("$COLOR_TOOL" "#ffffff" --output hex --name --no-copy --no-notify) assert_contains "$output" "(Mocked Color Name)" # 12. Invalid Color Input it "rejects invalid colors" output=$("$COLOR_TOOL" "invalid-color" --no-copy --no-notify 2>&1 || true) assert_contains "$output" "Error: Invalid color: invalid-color" # 13. Invalid Format Input it "rejects invalid formats" output=$("$COLOR_TOOL" "#000000" --output badfmt --no-copy --no-notify 2>&1 || true) assert_contains "$output" "Error: Invalid output format: badfmt" # 14. Installation Dependency Warnings it "warns about missing dependencies during install" mkdir -p "$TEST_DIR/install_bin" for cmd in bash cat echo printf mkdir mktemp chmod rm cp ln dirname readlink ps; do if [[ -e "/usr/bin/$cmd" || -L "/usr/bin/$cmd" ]]; then /usr/bin/ln -s "/usr/bin/$cmd" "$TEST_DIR/install_bin/$cmd" elif [[ -e "/bin/$cmd" || -L "/bin/$cmd" ]]; then /usr/bin/ln -s "/bin/$cmd" "$TEST_DIR/install_bin/$cmd" fi done output=$(PATH="$TEST_DIR/install_bin" HOME="$TEST_DIR" WAYLAND_DISPLAY="" XDG_CURRENT_DESKTOP="" KDE_FULL_SESSION="" "$COLOR_TOOL" --install 2>&1 || true) if [[ "$output" == *"python3 — required"* ]] && [[ "$output" == *"jq — required"* ]] && [[ "$output" == *"curl — required"* ]] && [[ "$output" == *"libnotify (notify-send)"* ]] && [[ "$output" == *"KDE Plasma on Wayland"* ]] && [[ "$output" == *"ImageMagick"* ]] && [[ "$output" == *"wl-clipboard"* ]]; then echo "PASS" passed=$((passed + 1)) else echo "FAIL" echo " Actual output: $output" fi # 15. Install — files placed in correct XDG directories it "installs files to XDG directories" # Clear any artifacts from earlier tests before verifying a clean install rm -rf "$XDG_DATA_HOME/color-tool" \ "$XDG_DATA_HOME/applications/color-tool.desktop" \ "$HOME/.local/bin" 2>/dev/null || true install_out=$("$COLOR_TOOL" --install 2>&1) if [[ -f "$XDG_DATA_HOME/color-tool/color-tool" ]] && [[ -f "$XDG_DATA_HOME/color-tool/wl-colorpicker-plasma.py" ]] && [[ -L "$HOME/.local/bin/color-tool" ]] && [[ -f "$XDG_DATA_HOME/applications/color-tool.desktop" ]] && [[ -f "$XDG_CONFIG_HOME/color-tool/config.toml" ]]; then echo "PASS" passed=$((passed + 1)) else echo "FAIL" echo " Install output: $install_out" echo " Files found:" /usr/bin/find "$TEST_DIR/.local" "$TEST_DIR/.config" \( -type f -o -type l \) 2>/dev/null | /usr/bin/sort || true fi # 16. Uninstall — removes installed files, config is kept in non-interactive mode it "uninstalls installed files (keeps config)" uninstall_out=$("$COLOR_TOOL" --uninstall &1) if [[ ! -e "$HOME/.local/bin/color-tool" ]] && [[ ! -L "$HOME/.local/bin/color-tool" ]] && [[ ! -d "$XDG_DATA_HOME/color-tool" ]] && [[ ! -f "$XDG_DATA_HOME/applications/color-tool.desktop" ]] && [[ -f "$XDG_CONFIG_HOME/color-tool/config.toml" ]]; then echo "PASS" passed=$((passed + 1)) else echo "FAIL" echo " Uninstall output: $uninstall_out" echo " Files still present:" /usr/bin/find "$TEST_DIR/.local" "$TEST_DIR/.config" \( -type f -o -type l \) 2>/dev/null | /usr/bin/sort || true fi # ── Cleanup ─────────────────────────────────────────────────────────────────── echo "---------------------------------------" echo "Result: $passed/$total tests passed." rm -rf "$TEST_DIR" if [[ $passed -ne $total ]]; then exit 1 fi