Skip to content

Instantly share code, notes, and snippets.

@aljorhythm
Created August 29, 2025 09:33
Show Gist options
  • Save aljorhythm/810050f8af6daefd1bc8bc18dbd05801 to your computer and use it in GitHub Desktop.
Save aljorhythm/810050f8af6daefd1bc8bc18dbd05801 to your computer and use it in GitHub Desktop.
Bash function to run only tests that changed vs main branch
#!/bin/bash
# πŸš€ Run Only Changed Tests - Git Diff Integration Test Runner
#
# Stop running entire test suites! This script automatically detects and runs only
# the integration tests that have changed in your git branch vs master.
#
# ⚑ SPEED UP YOUR WORKFLOW:
# β€’ Runs only modified .test.ts files in __integrations__/
# β€’ Extracts specific test names from git diff (it/describe blocks)
# β€’ Smart fallback to full file tests if extraction fails
# β€’ Watch mode for continuous testing
#
# πŸ’‘ PERFECT FOR:
# β€’ TDD/BDD development workflows
# β€’ Large test suites that take too long
# β€’ CI/CD optimization
# β€’ Mocha/Jest integration testing
#
# πŸ“– USAGE:
# tdiff-integration # Run changed tests once
# WATCH=true tdiff-integration # Watch mode with nodemon
#
# Keywords: git diff testing, selective test runner, integration tests,
# mocha grep, jest filter, test automation, ci optimization, tdd workflow
function tdiff-integration {
TEST_FILES=$(
git diff --name-only origin/master
git ls-files --others --exclude-standard
)
if [[ -z "$TEST_FILES" ]]; then
echo "No test files changed against master"
else
# Filter only .test.ts files in __integrations__ directory
INTEGRATION_FILES=$(echo "$TEST_FILES" | grep '__integrations__.*\.test\.ts$')
if [[ -z "$INTEGRATION_FILES" ]]; then
echo "No integration .test.ts files changed against master"
else
echo "Running specific integration test files:"
echo "$INTEGRATION_FILES"
cat .envrc.local
source .envrc.local
# Extract changed test names from git diff with improved regex
echo "πŸ” Starting test name extraction..."
CHANGED_TESTS=""
# Process each file individually by reading line by line
while IFS= read -r file; do
if [[ -n "$file" ]]; then
echo "πŸ“ Processing file: $file"
# Check if file exists to avoid git diff errors
if [[ -f "$file" ]]; then
echo "βœ… File exists, getting git diff..."
# Get added/modified lines that contain 'it(' or 'describe(' with better pattern matching
DIFF_OUTPUT=$(git diff origin/master -- "$file")
echo "πŸ“ Git diff output lines: $(echo "$DIFF_OUTPUT" | wc -l)"
ADDED_LINES=$(echo "$DIFF_OUTPUT" | grep '^+')
echo "βž• Added lines count: $(echo "$ADDED_LINES" | wc -l)"
TEST_LINES=$(echo "$ADDED_LINES" | grep -E '^\+\s*(it|describe)\s*\(')
echo "πŸ§ͺ Test lines found: $(echo "$TEST_LINES" | wc -l)"
if [[ -n "$TEST_LINES" ]]; then
echo "πŸ§ͺ Test lines content:"
echo "$TEST_LINES"
fi
echo "πŸ”§ Simple test name extraction..."
echo "Raw test lines:"
echo "$TEST_LINES"
# Super simple approach: use grep to extract quotes and backticks
TESTS=""
while IFS= read -r line; do
if [[ -n "$line" ]]; then
echo "πŸ”§ Processing line: '$line'"
# Extract anything in double quotes
quoted_text=$(echo "$line" | grep -o '"[^"]*"' | sed 's/"//g')
if [[ -n "$quoted_text" ]]; then
echo "βœ… Found quoted text: '$quoted_text'"
TESTS="$TESTS$quoted_text"$'\n'
else
# Try backticks - get first word after backtick
backtick_text=$(echo "$line" | grep -o '`[^`]*' | sed 's/`//' | cut -d' ' -f1 | cut -d':' -f1)
if [[ -n "$backtick_text" ]]; then
echo "βœ… Found backtick text: '$backtick_text'"
TESTS="$TESTS$backtick_text"$'\n'
else
echo "❌ No quotes found in line"
fi
fi
fi
done <<< "$TEST_LINES"
# Remove empty lines
TESTS=$(echo "$TESTS" | grep -v '^$')
echo "πŸ”§ All extracted tests:"
echo "$TESTS"
if [[ -n "$TESTS" ]]; then
echo "Found tests in $file:"
echo "$TESTS"
while IFS= read -r test_name; do
echo "πŸ” Processing test name: '$test_name'"
if [[ -n "$test_name" && "$test_name" != *"it("* && "$test_name" != *"describe("* && "$test_name" != *"+"* ]]; then
# Escape special regex characters for Mocha grep
escaped_test=$(echo "$test_name" | sed 's/[[\.*^$()+?{|\\]/\\&/g')
echo "βœ… Adding escaped test: '$escaped_test'"
if [[ -z "$CHANGED_TESTS" ]]; then
CHANGED_TESTS="$escaped_test"
else
CHANGED_TESTS="$CHANGED_TESTS|$escaped_test"
fi
else
echo "❌ Skipping test name: '$test_name'"
fi
done <<< "$TESTS"
else
echo "❌ No test names extracted from $file"
fi
else
echo "❌ File does not exist: $file"
fi
fi
done <<< "$INTEGRATION_FILES"
# Convert newline-separated files to space-separated, with quotes for safety
echo "πŸ“‹ Converting file list..."
FILE_LIST=""
while IFS= read -r file; do
if [[ -n "$file" ]]; then
FILE_LIST="$FILE_LIST \"$file\""
fi
done <<< "$INTEGRATION_FILES"
echo "πŸ“‹ Final file list: $FILE_LIST"
echo "πŸ§ͺ Final CHANGED_TESTS: '$CHANGED_TESTS'"
if [[ -n "$CHANGED_TESTS" ]]; then
# Remove leading pipe - no need for double escaping since we already escaped
PATTERN="$CHANGED_TESTS"
# CMD="npm run test:integration:only -- --grep \"($PATTERN)\" $FILE_LIST"
CMD="npm run test:integration:only -- $FILE_LIST"
echo "🎯 Running only changed test blocks with pattern: ($PATTERN)"
echo "πŸš€ Command: $CMD"
else
CMD="npm run test:integration:only -- $FILE_LIST"
echo "πŸ“‚ No specific test blocks found, running all tests in changed files"
echo "πŸš€ Command: $CMD"
fi
if [ "$WATCH" = "false" ]; then
eval $CMD
else
npx nodemon --exec "$CMD" --ext '.test.ts'
fi
fi
fi
}
@aljorhythm
Copy link
Author

Sample output

$ tdiff-integration 
Running specific integration test files:
__integrations__/users/api.test.ts
__integrations__/auth/service.test.ts
__integrations__/orders/controller.test.ts

πŸ” Starting test name extraction...
πŸ“ Processing file: __integrations__/users/api.test.ts
βœ… File exists, getting git diff...
πŸ“ Git diff output lines: 45
βž• Added lines count: 8
πŸ§ͺ Test lines found: 1
πŸ§ͺ Test lines content:
+    it("should validate user input", async () => {
πŸ”§ Processing line: '+    it("should validate user input", async () => {'
βœ… Found quoted text: 'should validate user input'
βœ… Adding escaped test: 'should validate user input'

πŸ“ Processing file: __integrations__/auth/service.test.ts
βœ… File exists, getting git diff...
πŸ“ Git diff output lines: 23
βž• Added lines count: 5
πŸ§ͺ Test lines found: 1
πŸ§ͺ Test lines content:
+  describe("#isAuthenticated", () => {
βœ… Found quoted text: '#isAuthenticated'
βœ… Adding escaped test: '#isAuthenticated'

πŸ“ Processing file: __integrations__/orders/controller.test.ts
βœ… File exists, getting git diff...
πŸ“ Git diff output lines: 12
βž• Added lines count: 3
πŸ§ͺ Test lines found: 0
❌ No test names extracted

πŸ§ͺ Final CHANGED_TESTS: 'should validate user input|#isAuthenticated'
🎯 Running only changed test blocks with pattern: (should validate user input|#isAuthenticated)
πŸš€ Command: mocha --grep "(should validate user input|#isAuthenticated)" users/api.test.ts auth/service.test.ts orders/controller.test.ts

> Running tests...
  Users API
    βœ“ should validate user input
  
  Auth Service  
    #isAuthenticated
      βœ“ should return true for valid token
      βœ“ should return false for expired token
      
  3 passing (45ms)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment