diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 44940d1..fd62050 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -66,3 +66,22 @@ jobs: - name: Run Python tests run: | for suite in tests/python/*/run.sh; do "$suite"; done + + javascript-tests: + name: JavaScript Tests + runs-on: ubuntu-latest + needs: shellcheck + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Prepare test environment + run: | + chmod +x javascript/*.sh + chmod +x tests/lib/*.bash + find tests/javascript -name "*.sh" -exec chmod +x {} + + + - name: Run JavaScript tests + run: | + for suite in tests/javascript/*/run.sh; do "$suite"; done diff --git a/javascript/check_eslint_all.sh b/javascript/check_eslint_all.sh new file mode 100755 index 0000000..6b74025 --- /dev/null +++ b/javascript/check_eslint_all.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs ESLint with --fix on the entire project (app/, components/, lib/, types/). +# No file arguments required — always checks all configured directories. +# Exits 1 if ESLint fails to fix issues, 0 on success. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +# Directories to check +LINT_DIRS=("app/" "components/" "lib/" "types/") + +echo "=== ESLint (full project) ===" + +echo "Fixing ESLint issues..." +if ! npx eslint "${LINT_DIRS[@]}" --fix; then + echo "ERROR: Failed to fix ESLint issues" + exit 1 +fi +echo "ESLint issues fixed!" + +exit 0 diff --git a/javascript/check_prettier.sh b/javascript/check_prettier.sh new file mode 100755 index 0000000..ad9f06c --- /dev/null +++ b/javascript/check_prettier.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs Prettier --write on staged TypeScript files. +# Receives files to check as arguments: ./check_prettier.sh file1.ts file2.ts +# Exits 1 if Prettier fails, 0 on success. Skips if no .ts files provided. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +# Parse arguments +FILES=() + +# Filter only .ts files +RELEVANT_FILES=() +for file in "${FILES[@]}"; do + if [[ "$file" == *.ts ]]; then + if [ -f "$file" ]; then + RELEVANT_FILES+=("$file") + fi + fi +done + +# Skip if no relevant files +if [ ${#RELEVANT_FILES[@]} -eq 0 ]; then + echo "=== Code Style Check ===" + echo "No TypeScript files to check, skipping..." + exit 0 +fi + +echo "=== Code Style Check (${#RELEVANT_FILES[@]} files) ===" + +echo "Fixing code style issues..." +if ! npx prettier --write "${RELEVANT_FILES[@]}"; then + echo "ERROR: Failed to fix code style" + exit 1 +fi +echo "Code style fixed!" + +exit 0 diff --git a/javascript/check_prettier_all.sh b/javascript/check_prettier_all.sh new file mode 100755 index 0000000..6afa777 --- /dev/null +++ b/javascript/check_prettier_all.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs Prettier --write on all TypeScript files in the project. +# No file arguments required — uses glob patterns for app/, components/, lib/, etc. +# Exits 1 if Prettier fails, 0 on success. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +echo "Fixing code style issues..." +if ! npx prettier --write "app/**/*.{ts,tsx}" "components/**/*.{ts,tsx}" "lib/**/*.ts" "services/**/*.ts" "types/**/*.ts"; then + echo "ERROR: Failed to fix code style" + exit 1 +fi +echo "Code style fixed!" + +exit 0 diff --git a/javascript/check_test_coverage.sh b/javascript/check_test_coverage.sh new file mode 100755 index 0000000..58a161d --- /dev/null +++ b/javascript/check_test_coverage.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs vitest with optional --watch and --coverage flags for specified files. +# Receives files as arguments: ./check_test_coverage.sh file1.ts [--watch] [--coverage] +# Exits 1 if tests fail, 0 on success. Runs full suite when no files provided. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" + +cd "$PROJECT_ROOT" + +# Parse arguments +WATCH=false +COVERAGE=false +FILES=() + +for arg in "$@"; do + case $arg in + --watch) + WATCH=true + ;; + --coverage) + COVERAGE=true + ;; + *) + FILES+=("$arg") + ;; + esac +done + +# Filter only test files or source files that have tests +RELEVANT_FILES=() +for file in "${FILES[@]}"; do + if [[ "$file" == *.ts ]]; then + if [ -f "$file" ]; then + RELEVANT_FILES+=("$file") + fi + fi +done + +# Determine test command +if [ ${#RELEVANT_FILES[@]} -eq 0 ]; then + echo "=== Tests (full suite) ===" + + if [ "$WATCH" = true ]; then + echo "Running tests in watch mode..." + npx vitest + elif [ "$COVERAGE" = true ]; then + echo "Running tests with coverage..." + if ! npx vitest run --coverage; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed with coverage!" + else + echo "Running tests..." + if ! npx vitest run; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed!" + fi +else + echo "=== Tests (related to ${#RELEVANT_FILES[@]} changed files) ===" + + if [ "$WATCH" = true ]; then + echo "Running related tests in watch mode..." + npx vitest --related "${RELEVANT_FILES[@]}" + elif [ "$COVERAGE" = true ]; then + echo "Running related tests with coverage..." + if ! npx vitest run --related "${RELEVANT_FILES[@]}" --coverage; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed with coverage!" + else + echo "Running related tests..." + if ! npx vitest run --related "${RELEVANT_FILES[@]}"; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed!" + fi +fi + +exit 0 diff --git a/javascript/check_tests.sh b/javascript/check_tests.sh new file mode 100755 index 0000000..f14d959 --- /dev/null +++ b/javascript/check_tests.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs vitest for changed TypeScript files or from git staged files. +# Receives files as arguments: ./check_tests.sh file1.ts file2.ts +# Exits 1 if tests fail, 0 on success. Skips files matching SKIP_PATTERNS. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +# Patterns to skip +SKIP_PATTERNS=("tests/" ".test.ts" ".config.ts" ".config.mjs" "types/" ".d.ts" "layout.tsx" "page.tsx" "loading.tsx" "error.tsx" "globals.css" "providers/" "components/ui/" "prisma/") + +should_skip() { + local file="$1" + for pattern in "${SKIP_PATTERNS[@]}"; do + [[ "$file" == *"$pattern"* ]] && return 0 + done + return 1 +} + +# Get test path: source.ts -> tests/source.test.ts +get_test_path() { + local file="$1" + local base="${file%.ts}" + base="${base%.tsx}" + echo "tests/${base}.test.ts" +} + +# Get files to check +FILES=() +if [ $# -eq 0 ]; then + while IFS= read -r line; do + [[ "$line" == *.ts || "$line" == *.tsx ]] && FILES+=("$line") + done < <(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || true) + [ ${#FILES[@]} -eq 0 ] && echo "No staged TypeScript files" && exit 0 +else + for arg in "$@"; do + [[ "$arg" == *.ts || "$arg" == *.tsx ]] && FILES+=("$arg") + done +fi + +# Collect tests to run +TESTS_TO_RUN=() + +for file in "${FILES[@]}"; do + [ ! -f "$file" ] && continue + + # If it's already a test file, add it directly + if [[ "$file" == *.test.ts ]]; then + [ -f "$file" ] && TESTS_TO_RUN+=("$file") + continue + fi + + should_skip "$file" && continue + + test_path=$(get_test_path "$file") + [ -f "$test_path" ] && TESTS_TO_RUN+=("$test_path") +done + +# Remove duplicates +mapfile -t TESTS_TO_RUN < <(printf '%s\n' "${TESTS_TO_RUN[@]}" | sort -u) + +if [ ${#TESTS_TO_RUN[@]} -eq 0 ]; then + echo "No tests to run for changed files" + exit 0 +fi + +echo "=== Running tests for changed files ===" +echo "Tests: ${TESTS_TO_RUN[*]}" +echo "" + +npx vitest run "${TESTS_TO_RUN[@]}" diff --git a/javascript/check_tests_all.sh b/javascript/check_tests_all.sh new file mode 100755 index 0000000..7d91b9d --- /dev/null +++ b/javascript/check_tests_all.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs the full vitest test suite for the project. +# No file arguments required — runs all tests via `npx vitest run`. +# Exits 1 if tests fail, 0 on success. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +echo "=== Running tests ===" +echo "" + +npx vitest run diff --git a/javascript/check_tests_exist.sh b/javascript/check_tests_exist.sh new file mode 100755 index 0000000..689e4f0 --- /dev/null +++ b/javascript/check_tests_exist.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Checks that each staged TypeScript source file has a corresponding test file. +# Receives files as arguments or reads from git staged files if none provided. +# Exits 1 if any source file is missing its tests/...test.ts counterpart. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +# Patterns to skip (no tests required) +SKIP_PATTERNS=("tests/" ".test.ts" ".config.ts" ".config.mjs" "types/" ".d.ts" "layout.tsx" "page.tsx" "loading.tsx" "error.tsx" "globals.css" "providers/" "components/ui/" "prisma/") + +should_skip() { + local file="$1" + for pattern in "${SKIP_PATTERNS[@]}"; do + [[ "$file" == *"$pattern"* ]] && return 0 + done + return 1 +} + +# Get test path: source.ts -> tests/source.test.ts +get_test_path() { + local file="$1" + local base="${file%.ts}" + base="${base%.tsx}" + echo "tests/${base}.test.ts" +} + +# Get files to check +FILES=() +if [ $# -eq 0 ]; then + while IFS= read -r line; do + [[ "$line" == *.ts || "$line" == *.tsx ]] && FILES+=("$line") + done < <(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || true) + [ ${#FILES[@]} -eq 0 ] && echo "No staged TypeScript files" && exit 0 +else + for arg in "$@"; do + [[ "$arg" == *.ts || "$arg" == *.tsx ]] && FILES+=("$arg") + done +fi + +echo "=== Test Coverage Check ===" +echo "" + +missing=() +found=() +skipped=() + +for file in "${FILES[@]}"; do + [ ! -f "$file" ] && continue + should_skip "$file" && { skipped+=("$file"); continue; } + + test_path=$(get_test_path "$file") + + if [ -f "$test_path" ]; then + found+=("$file") + else + missing+=("$file → $test_path") + fi +done + +[ ${#found[@]} -gt 0 ] && echo -e "Has tests:" && printf ' %s\n' "${found[@]}" && echo "" +[ ${#skipped[@]} -gt 0 ] && echo -e "Skipped:" && printf ' %s\n' "${skipped[@]}" && echo "" + +if [ ${#missing[@]} -gt 0 ]; then + echo -e "Missing tests:" + printf ' %s\n' "${missing[@]}" + echo "" + echo -e "ERROR: ${#missing[@]} file(s) missing tests" + exit 1 +fi + +echo -e "All files have tests!" diff --git a/javascript/check_tsc_all.sh b/javascript/check_tsc_all.sh new file mode 100755 index 0000000..38fa34f --- /dev/null +++ b/javascript/check_tsc_all.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs TypeScript type checking on the entire project using tsconfig.check.json. +# No file arguments required — checks all files configured in tsconfig. +# Exits 1 if type check fails, 0 on success. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +# TypeScript config +TS_CONFIG="tsconfig.check.json" + +echo "=== TypeScript ===" +echo "Running type check..." +if ! npx tsc --project "$TS_CONFIG"; then + echo "ERROR: TypeScript check failed" + exit 1 +fi +echo "TypeScript check passed!" + +exit 0 diff --git a/javascript/check_vitest.sh b/javascript/check_vitest.sh new file mode 100755 index 0000000..2be1340 --- /dev/null +++ b/javascript/check_vitest.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs vitest for specified files or the full suite. Supports --watch and --coverage. +# Receives files as arguments: ./check_vitest.sh file1.ts [--watch] [--coverage] +# Exits 1 if tests fail, 0 on success. Runs full suite when no files provided. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +# Parse arguments +WATCH=false +COVERAGE=false +FILES=() + +for arg in "$@"; do + case $arg in + --watch) + WATCH=true + ;; + --coverage) + COVERAGE=true + ;; + *) + FILES+=("$arg") + ;; + esac +done + +# Filter only test files or source files that have tests +RELEVANT_FILES=() +for file in "${FILES[@]}"; do + if [[ "$file" == *.ts ]]; then + if [ -f "$file" ]; then + RELEVANT_FILES+=("$file") + fi + fi +done + +# Determine test command +if [ ${#RELEVANT_FILES[@]} -eq 0 ]; then + echo "=== Tests (full suite) ===" + + if [ "$WATCH" = true ]; then + echo "Running tests in watch mode..." + npx vitest + elif [ "$COVERAGE" = true ]; then + echo "Running tests with coverage..." + if ! npx vitest run --coverage; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed with coverage!" + else + echo "Running tests..." + if ! npx vitest run; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed!" + fi +else + echo "=== Tests (related to ${#RELEVANT_FILES[@]} changed files) ===" + + if [ "$WATCH" = true ]; then + echo "Running related tests in watch mode..." + npx vitest --related "${RELEVANT_FILES[@]}" + elif [ "$COVERAGE" = true ]; then + echo "Running related tests with coverage..." + if ! npx vitest run --related "${RELEVANT_FILES[@]}" --coverage; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed with coverage!" + else + echo "Running related tests..." + if ! npx vitest run --related "${RELEVANT_FILES[@]}"; then + echo "ERROR: Tests failed" + exit 1 + fi + echo "Tests passed!" + fi +fi + +exit 0 diff --git a/javascript/check_vitest_all.sh b/javascript/check_vitest_all.sh new file mode 100755 index 0000000..65dde57 --- /dev/null +++ b/javascript/check_vitest_all.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs the full vitest test suite. +# No file arguments required — runs `npx vitest run`. +# Exits 1 if tests fail, 0 on success. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT" + +echo "Running tests..." +if ! npx vitest run; then + echo "ERROR: Tests failed" + exit 1 +fi + +echo "Tests passed!" + +exit 0 diff --git a/tests/javascript/eslint_all/cases/01_eslint_passes.sh b/tests/javascript/eslint_all/cases/01_eslint_passes.sh new file mode 100755 index 0000000..dc19e08 --- /dev/null +++ b/tests/javascript/eslint_all/cases/01_eslint_passes.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: ESLint passes when npx exits 0 +# Expected: exit 0, message about issues fixed +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "eslint_all_passes" + +mock_npx 0 "All good" + +run_hook_at_depth "javascript/check_eslint_all.sh" 2 + +assert_exit_code 0 +assert_output_contains "ESLint issues fixed" + +test_passed diff --git a/tests/javascript/eslint_all/cases/02_eslint_fails.sh b/tests/javascript/eslint_all/cases/02_eslint_fails.sh new file mode 100755 index 0000000..a8f0594 --- /dev/null +++ b/tests/javascript/eslint_all/cases/02_eslint_fails.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: ESLint fails when npx exits 1 +# Expected: exit 1, error message +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "eslint_all_fails" + +mock_npx 1 "Lint errors found" + +run_hook_at_depth "javascript/check_eslint_all.sh" 2 + +assert_exit_code 1 +assert_output_contains "ERROR" + +test_passed diff --git a/tests/javascript/eslint_all/run.sh b/tests/javascript/eslint_all/run.sh new file mode 100755 index 0000000..6b2341b --- /dev/null +++ b/tests/javascript/eslint_all/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all check_eslint_all hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "ESLint all tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/javascript/tests_exist/cases/01_no_files_exits_zero.sh b/tests/javascript/tests_exist/cases/01_no_files_exits_zero.sh new file mode 100755 index 0000000..5341c90 --- /dev/null +++ b/tests/javascript/tests_exist/cases/01_no_files_exits_zero.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No files provided and no staged files exits with 0 +# Expected: exit 0, message about no staged TypeScript files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "tests_exist_no_files" + +run_hook_at_depth "javascript/check_tests_exist.sh" 2 + +assert_exit_code 0 +assert_output_contains "No staged TypeScript files" + +test_passed diff --git a/tests/javascript/tests_exist/cases/02_non_ts_files_ignored.sh b/tests/javascript/tests_exist/cases/02_non_ts_files_ignored.sh new file mode 100755 index 0000000..19d286b --- /dev/null +++ b/tests/javascript/tests_exist/cases/02_non_ts_files_ignored.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Non-TypeScript files are ignored +# Expected: exit 0, message about no staged TypeScript files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "tests_exist_non_ts_ignored" + +create_fixture "app/utils.js" "export const foo = 1;" + +run_hook_at_depth "javascript/check_tests_exist.sh" 2 "app/utils.js" + +assert_exit_code 0 +assert_output_contains "All files have tests" + +test_passed diff --git a/tests/javascript/tests_exist/cases/03_file_with_test_passes.sh b/tests/javascript/tests_exist/cases/03_file_with_test_passes.sh new file mode 100755 index 0000000..20befed --- /dev/null +++ b/tests/javascript/tests_exist/cases/03_file_with_test_passes.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: TypeScript file with matching test file passes +# Expected: exit 0, message about all files having tests +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "tests_exist_file_with_test" + +create_fixture "app/utils.ts" "export const foo = 1;" +create_fixture "tests/app/utils.test.ts" "test('foo', () => {});" + +run_hook_at_depth "javascript/check_tests_exist.sh" 2 "app/utils.ts" + +assert_exit_code 0 +assert_output_contains "All files have tests" + +test_passed diff --git a/tests/javascript/tests_exist/cases/04_file_missing_test_fails.sh b/tests/javascript/tests_exist/cases/04_file_missing_test_fails.sh new file mode 100755 index 0000000..4bc642a --- /dev/null +++ b/tests/javascript/tests_exist/cases/04_file_missing_test_fails.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: TypeScript file without matching test file fails +# Expected: exit 1, message about missing tests +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "tests_exist_missing_test" + +create_fixture "app/utils.ts" "export const foo = 1;" + +run_hook_at_depth "javascript/check_tests_exist.sh" 2 "app/utils.ts" + +assert_exit_code 1 +assert_output_contains "Missing tests" + +test_passed diff --git a/tests/javascript/tests_exist/cases/05_skip_patterns_work.sh b/tests/javascript/tests_exist/cases/05_skip_patterns_work.sh new file mode 100755 index 0000000..639cce1 --- /dev/null +++ b/tests/javascript/tests_exist/cases/05_skip_patterns_work.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Files matching skip patterns are skipped +# Expected: exit 0, file listed as skipped +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "tests_exist_skip_patterns" + +create_fixture "app/next.config.ts" "export default {};" + +run_hook_at_depth "javascript/check_tests_exist.sh" 2 "app/next.config.ts" + +assert_exit_code 0 +assert_output_contains "Skipped" + +test_passed diff --git a/tests/javascript/tests_exist/run.sh b/tests/javascript/tests_exist/run.sh new file mode 100755 index 0000000..4291fd1 --- /dev/null +++ b/tests/javascript/tests_exist/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all check_tests_exist hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Tests exist tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/javascript/vitest/cases/01_no_files_runs_full_suite.sh b/tests/javascript/vitest/cases/01_no_files_runs_full_suite.sh new file mode 100755 index 0000000..e52b076 --- /dev/null +++ b/tests/javascript/vitest/cases/01_no_files_runs_full_suite.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: No files runs full test suite +# Expected: exit 0, message about full suite +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "vitest_no_files_full_suite" + +mock_npx 0 "Tests passed" + +run_hook_at_depth "javascript/check_vitest.sh" 2 + +assert_exit_code 0 +assert_output_contains "full suite" + +test_passed diff --git a/tests/javascript/vitest/cases/02_ts_files_runs_related.sh b/tests/javascript/vitest/cases/02_ts_files_runs_related.sh new file mode 100755 index 0000000..9f05f56 --- /dev/null +++ b/tests/javascript/vitest/cases/02_ts_files_runs_related.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Passing .ts files runs related tests +# Expected: exit 0, message about related files +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "vitest_ts_files_related" + +mock_npx 0 "Tests passed" + +create_fixture "app/utils.ts" "export const foo = 1;" + +run_hook_at_depth "javascript/check_vitest.sh" 2 "app/utils.ts" + +assert_exit_code 0 +assert_output_contains "related" + +test_passed diff --git a/tests/javascript/vitest/cases/03_test_failure_exits_one.sh b/tests/javascript/vitest/cases/03_test_failure_exits_one.sh new file mode 100755 index 0000000..4260355 --- /dev/null +++ b/tests/javascript/vitest/cases/03_test_failure_exits_one.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Test: Test failure exits with 1 +# Expected: exit 1, error message +# ------------------------------------------------------------------------------ + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/../../../lib/test_helper.bash" + +setup_test_env "vitest_failure_exits_one" + +mock_npx 1 "Test failed" + +run_hook_at_depth "javascript/check_vitest.sh" 2 + +assert_exit_code 1 +assert_output_contains "ERROR" + +test_passed diff --git a/tests/javascript/vitest/run.sh b/tests/javascript/vitest/run.sh new file mode 100755 index 0000000..35f27c5 --- /dev/null +++ b/tests/javascript/vitest/run.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# ------------------------------------------------------------------------------ +# Runs all check_vitest hook test cases. +# Executes each case script in the cases/ directory. +# ------------------------------------------------------------------------------ + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PASSED=0 +FAILED=0 + +for case_file in "$SCRIPT_DIR"/cases/*.sh; do + [[ -f "$case_file" ]] || continue + + if "$case_file"; then + ((PASSED++)) || true + else + ((FAILED++)) || true + fi +done + +echo "" +echo "Vitest tests: $PASSED passed, $FAILED failed" + +[[ $FAILED -eq 0 ]] diff --git a/tests/lib/test_helper.bash b/tests/lib/test_helper.bash index 0809022..95d78da 100755 --- a/tests/lib/test_helper.bash +++ b/tests/lib/test_helper.bash @@ -212,6 +212,20 @@ mock_git_branch() { git -C "$TEST_DIR" checkout -q "$branch_name" 2>/dev/null || true } +mock_npx() { + local exit_code="$1" + local output="${2:-}" + + mkdir -p "$TEST_DIR/bin" + cat > "$TEST_DIR/bin/npx" << EOF +#!/bin/bash +echo "$output" +exit $exit_code +EOF + chmod +x "$TEST_DIR/bin/npx" + export PATH="$TEST_DIR/bin:$PATH" +} + mock_docker_compose() { mkdir -p "$TEST_DIR/bin" cat > "$TEST_DIR/bin/docker" << 'EOF' @@ -255,6 +269,40 @@ run_hook() { set -e } +run_hook_at_depth() { + local hook_path="$1" + local depth="$2" + shift 2 + local args=("$@") + + # Get absolute path to hook + local script_dir + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + local full_hook_path="$script_dir/$hook_path" + + if [[ ! -f "$full_hook_path" ]]; then + echo -e "${RED}[ERROR]${NC} Hook not found: $full_hook_path" + return 1 + fi + + # Build nested directory path inside TEST_DIR + local nested_dir="$TEST_DIR" + for ((i = 0; i < depth; i++)); do + nested_dir="$nested_dir/level_$i" + done + mkdir -p "$nested_dir" + + # Copy hook script into nested directory + cp "$full_hook_path" "$nested_dir/hook.sh" + chmod +x "$nested_dir/hook.sh" + + # Run hook and capture output + set +e + LAST_OUTPUT=$("$nested_dir/hook.sh" "${args[@]}" 2>&1) + LAST_EXIT_CODE=$? + set -e +} + # ----------------------------------------- # Assertions # -----------------------------------------