mirror of
https://github.com/ivuorinen/nvim-shellspec.git
synced 2026-01-25 19:14:08 +00:00
feat: implement dynamic test generation and resolve pre-commit conflicts
This commit is contained in:
290
Makefile
Normal file
290
Makefile
Normal file
@@ -0,0 +1,290 @@
|
||||
# Makefile for nvim-shellspec
|
||||
# Provides help, linting, testing, and release functionality
|
||||
|
||||
# Colors for output
|
||||
RED := \033[0;31m
|
||||
GREEN := \033[0;32m
|
||||
YELLOW := \033[1;33m
|
||||
BLUE := \033[0;34m
|
||||
NC := \033[0m # No Color
|
||||
|
||||
# Version files
|
||||
VERSION_LUA := lua/shellspec/init.lua
|
||||
VERSION_VIM := plugin/shellspec.vim
|
||||
VERSION_BIN := bin/shellspec-format
|
||||
|
||||
# Commands
|
||||
MAKE := make
|
||||
PRE_COMMIT := pre-commit
|
||||
TEST_RUNNER := ./tests/run_tests.sh
|
||||
|
||||
# Default target
|
||||
.PHONY: help
|
||||
help: ## Display this help message
|
||||
@echo "$(BLUE)nvim-shellspec Makefile$(NC)"
|
||||
@echo "$(BLUE)==========================================$(NC)"
|
||||
@echo ""
|
||||
@echo "$(GREEN)Available targets:$(NC)"
|
||||
@echo ""
|
||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " $(YELLOW)%-20s$(NC) %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||
@echo ""
|
||||
@echo "$(GREEN)Current versions:$(NC)"
|
||||
@$(MAKE) --no-print-directory version-check
|
||||
@echo ""
|
||||
@echo "$(GREEN)Usage examples:$(NC)"
|
||||
@echo " $(YELLOW)make test$(NC) # Run all tests"
|
||||
@echo " $(YELLOW)make lint$(NC) # Run all linters"
|
||||
@echo " $(YELLOW)make release-patch$(NC) # Bump patch version and create tag"
|
||||
@echo ""
|
||||
|
||||
.PHONY: check
|
||||
check: ## Quick health check (verify tools and version consistency)
|
||||
@echo "$(BLUE)Running health check...$(NC)"
|
||||
@echo ""
|
||||
@echo "$(GREEN)Checking required tools:$(NC)"
|
||||
@which pre-commit >/dev/null 2>&1 && echo " ✓ pre-commit found" || echo " $(RED)✗ pre-commit not found$(NC)"
|
||||
@which git >/dev/null 2>&1 && echo " ✓ git found" || echo " $(RED)✗ git not found$(NC)"
|
||||
@which bash >/dev/null 2>&1 && echo " ✓ bash found" || echo " $(RED)✗ bash not found$(NC)"
|
||||
@test -f $(TEST_RUNNER) && echo " ✓ test runner found" || echo " $(RED)✗ test runner not found$(NC)"
|
||||
@echo ""
|
||||
@echo "$(GREEN)Version consistency:$(NC)"
|
||||
@$(MAKE) --no-print-directory version-check
|
||||
@echo ""
|
||||
|
||||
.PHONY: version
|
||||
version: version-check ## Display current versions
|
||||
|
||||
.PHONY: version-check
|
||||
version-check: ## Check version consistency across files
|
||||
@echo "$(GREEN)Version information:$(NC)"
|
||||
@lua_version=$$(grep '_VERSION = ' $(VERSION_LUA) | sed 's/.*"\(.*\)".*/\1/'); \
|
||||
vim_version=$$(grep "g:shellspec_version = " $(VERSION_VIM) | sed "s/.*'\(.*\)'.*/\1/"); \
|
||||
bin_version=$$(grep 'echo "shellspec-format ' $(VERSION_BIN) | sed 's/.*shellspec-format \([0-9.]*\).*/\1/'); \
|
||||
echo " Lua module: $$lua_version"; \
|
||||
echo " VimScript: $$vim_version"; \
|
||||
echo " Binary script: $$bin_version"; \
|
||||
if [ "$$lua_version" = "$$vim_version" ] && [ "$$vim_version" = "$$bin_version" ]; then \
|
||||
echo " $(GREEN)✓ All versions match$(NC)"; \
|
||||
else \
|
||||
echo " $(RED)✗ Version mismatch detected$(NC)"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# Linting targets
|
||||
.PHONY: lint
|
||||
lint: ## Run all linters
|
||||
@echo "$(BLUE)Running all linters...$(NC)"
|
||||
$(PRE_COMMIT) run --all-files
|
||||
|
||||
.PHONY: lint-fix
|
||||
lint-fix: format ## Run linters with auto-fix (alias for format)
|
||||
|
||||
.PHONY: format
|
||||
format: ## Format all code (auto-fix where possible)
|
||||
@echo "$(BLUE)Formatting all code...$(NC)"
|
||||
$(PRE_COMMIT) run --all-files
|
||||
|
||||
.PHONY: lint-lua
|
||||
lint-lua: ## Format Lua code with StyLua
|
||||
@echo "$(BLUE)Formatting Lua code...$(NC)"
|
||||
$(PRE_COMMIT) run stylua-github --all-files
|
||||
|
||||
.PHONY: lint-shell
|
||||
lint-shell: ## Lint shell scripts with ShellCheck and format with shfmt
|
||||
@echo "$(BLUE)Linting shell scripts...$(NC)"
|
||||
$(PRE_COMMIT) run shellcheck --all-files
|
||||
$(PRE_COMMIT) run shfmt --all-files
|
||||
|
||||
.PHONY: lint-markdown
|
||||
lint-markdown: ## Lint and format Markdown files
|
||||
@echo "$(BLUE)Linting Markdown files...$(NC)"
|
||||
$(PRE_COMMIT) run markdownlint --all-files
|
||||
|
||||
.PHONY: lint-yaml
|
||||
lint-yaml: ## Lint YAML files
|
||||
@echo "$(BLUE)Linting YAML files...$(NC)"
|
||||
$(PRE_COMMIT) run yamllint --all-files
|
||||
|
||||
# Testing targets
|
||||
.PHONY: test
|
||||
test: ## Run complete test suite
|
||||
@echo "$(BLUE)Running complete test suite...$(NC)"
|
||||
$(TEST_RUNNER)
|
||||
|
||||
.PHONY: test-unit
|
||||
test-unit: ## Run only Lua unit tests
|
||||
@echo "$(BLUE)Running unit tests...$(NC)"
|
||||
cd tests && timeout 30 nvim --headless -u NONE -c "set rtp+=.." -c "luafile format_spec.lua" -c "quit"
|
||||
|
||||
.PHONY: test-integration
|
||||
test-integration: ## Run integration tests
|
||||
@echo "$(BLUE)Running integration tests...$(NC)"
|
||||
cd tests && timeout 30 ./integration_test.sh
|
||||
|
||||
.PHONY: test-golden
|
||||
test-golden: ## Run golden master tests
|
||||
@echo "$(BLUE)Running golden master tests...$(NC)"
|
||||
cd tests && timeout 30 ./golden_master_test.sh
|
||||
|
||||
.PHONY: test-bin
|
||||
test-bin: ## Run standalone formatter tests
|
||||
@echo "$(BLUE)Running standalone formatter tests...$(NC)"
|
||||
cd tests && ./bin_format_spec.sh
|
||||
|
||||
# Release targets
|
||||
.PHONY: release
|
||||
release: ## Interactive release (prompts for version type)
|
||||
@echo "$(BLUE)Interactive Release$(NC)"
|
||||
@echo ""
|
||||
@echo "Select release type:"
|
||||
@echo " 1) $(GREEN)patch$(NC) (2.0.0 → 2.0.1) - Bug fixes"
|
||||
@echo " 2) $(YELLOW)minor$(NC) (2.0.0 → 2.1.0) - New features"
|
||||
@echo " 3) $(RED)major$(NC) (2.0.0 → 3.0.0) - Breaking changes"
|
||||
@echo ""
|
||||
@read -p "Enter choice (1-3): " choice; \
|
||||
case $$choice in \
|
||||
1) $(MAKE) release-patch ;; \
|
||||
2) $(MAKE) release-minor ;; \
|
||||
3) $(MAKE) release-major ;; \
|
||||
*) echo "$(RED)Invalid choice$(NC)"; exit 1 ;; \
|
||||
esac
|
||||
|
||||
.PHONY: release-patch
|
||||
release-patch: ## Bump patch version (X.Y.Z → X.Y.Z+1)
|
||||
@$(MAKE) --no-print-directory _release TYPE=patch
|
||||
|
||||
.PHONY: release-minor
|
||||
release-minor: ## Bump minor version (X.Y.Z → X.Y+1.0)
|
||||
@$(MAKE) --no-print-directory _release TYPE=minor
|
||||
|
||||
.PHONY: release-major
|
||||
release-major: ## Bump major version (X.Y.Z → X+1.0.0)
|
||||
@$(MAKE) --no-print-directory _release TYPE=major
|
||||
|
||||
.PHONY: _release
|
||||
_release: ## Internal release target (use release-* targets instead)
|
||||
@if [ "$(TYPE)" = "" ]; then echo "$(RED)Error: TYPE not specified$(NC)"; exit 1; fi
|
||||
@echo "$(BLUE)Starting $(TYPE) release...$(NC)"
|
||||
@echo ""
|
||||
|
||||
# Check git status
|
||||
@echo "$(GREEN)Checking git status...$(NC)"
|
||||
@if [ -n "$$(git status --porcelain)" ]; then \
|
||||
echo "$(RED)Error: Working directory not clean$(NC)"; \
|
||||
git status --short; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo " ✓ Working directory is clean"
|
||||
|
||||
# Check version consistency
|
||||
@echo ""
|
||||
@echo "$(GREEN)Checking version consistency...$(NC)"
|
||||
@$(MAKE) --no-print-directory version-check
|
||||
|
||||
# Run tests
|
||||
@echo ""
|
||||
@echo "$(GREEN)Running tests...$(NC)"
|
||||
@$(MAKE) --no-print-directory test
|
||||
|
||||
# Run linters
|
||||
@echo ""
|
||||
@echo "$(GREEN)Running linters...$(NC)"
|
||||
@$(MAKE) --no-print-directory lint
|
||||
|
||||
# Calculate new version
|
||||
@echo ""
|
||||
@echo "$(GREEN)Calculating new version...$(NC)"
|
||||
@current_version=$$(grep '_VERSION = ' $(VERSION_LUA) | sed 's/.*"\(.*\)".*/\1/'); \
|
||||
echo " Current version: $$current_version"; \
|
||||
new_version=$$(echo "$$current_version" | awk -F. -v type=$(TYPE) '{ \
|
||||
if (type == "major") printf "%d.0.0", $$1+1; \
|
||||
else if (type == "minor") printf "%d.%d.0", $$1, $$2+1; \
|
||||
else if (type == "patch") printf "%d.%d.%d", $$1, $$2, $$3+1; \
|
||||
}'); \
|
||||
echo " New version: $$new_version"; \
|
||||
echo ""; \
|
||||
read -p "Continue with release? (y/N): " confirm; \
|
||||
if [ "$$confirm" != "y" ] && [ "$$confirm" != "Y" ]; then \
|
||||
echo "$(YELLOW)Release cancelled$(NC)"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
echo ""; \
|
||||
echo "$(GREEN)Updating version in files...$(NC)"; \
|
||||
sed -i.bak "s/M._VERSION = \".*\"/M._VERSION = \"$$new_version\"/" $(VERSION_LUA) && rm $(VERSION_LUA).bak; \
|
||||
sed -i.bak "s/let g:shellspec_version = '.*'/let g:shellspec_version = '$$new_version'/" $(VERSION_VIM) && rm $(VERSION_VIM).bak; \
|
||||
sed -i.bak "s/shellspec-format [0-9.]*/shellspec-format $$new_version/" $(VERSION_BIN) && rm $(VERSION_BIN).bak; \
|
||||
echo " ✓ Updated $(VERSION_LUA)"; \
|
||||
echo " ✓ Updated $(VERSION_VIM)"; \
|
||||
echo " ✓ Updated $(VERSION_BIN)"; \
|
||||
echo ""; \
|
||||
echo "$(GREEN)Creating git commit...$(NC)"; \
|
||||
git add $(VERSION_LUA) $(VERSION_VIM) $(VERSION_BIN); \
|
||||
git commit -m "chore: bump version to $$new_version"; \
|
||||
echo " ✓ Created commit"; \
|
||||
echo ""; \
|
||||
echo "$(GREEN)Creating git tag...$(NC)"; \
|
||||
git tag -a "v$$new_version" -m "Release version $$new_version"; \
|
||||
echo " ✓ Created tag v$$new_version"; \
|
||||
echo ""; \
|
||||
echo "$(GREEN)$(TYPE) release completed successfully!$(NC)"; \
|
||||
echo ""; \
|
||||
echo "$(BLUE)Next steps:$(NC)"; \
|
||||
echo " 1. Review the changes: $(YELLOW)git show$(NC)"; \
|
||||
echo " 2. Push the release: $(YELLOW)git push origin main --tags$(NC)"; \
|
||||
echo " 3. Create GitHub release from tag v$$new_version"; \
|
||||
echo ""
|
||||
|
||||
# Utility targets
|
||||
.PHONY: clean
|
||||
clean: ## Remove temporary files and test artifacts
|
||||
@echo "$(BLUE)Cleaning temporary files...$(NC)"
|
||||
find . -name "*.bak" -delete
|
||||
find . -name "*.tmp" -delete
|
||||
find /tmp -name "*shellspec*" -delete 2>/dev/null || true
|
||||
find /var/folders -name "*shellspec*" -delete 2>/dev/null || true
|
||||
@echo " ✓ Cleaned temporary files"
|
||||
|
||||
.PHONY: install
|
||||
install: ## Install pre-commit hooks
|
||||
@echo "$(BLUE)Installing pre-commit hooks...$(NC)"
|
||||
$(PRE_COMMIT) install
|
||||
@echo " ✓ Pre-commit hooks installed"
|
||||
|
||||
# Development convenience targets
|
||||
.PHONY: dev-setup
|
||||
dev-setup: install ## Set up development environment
|
||||
@echo "$(BLUE)Setting up development environment...$(NC)"
|
||||
@$(MAKE) --no-print-directory check
|
||||
@echo ""
|
||||
@echo "$(GREEN)Development environment ready!$(NC)"
|
||||
|
||||
.PHONY: ci
|
||||
ci: check test lint ## Run CI pipeline (check, test, lint)
|
||||
@echo ""
|
||||
@echo "$(GREEN)CI pipeline completed successfully!$(NC)"
|
||||
|
||||
# Debug targets
|
||||
.PHONY: debug
|
||||
debug: ## Show debug information
|
||||
@echo "$(BLUE)Debug Information$(NC)"
|
||||
@echo "$(BLUE)==================$(NC)"
|
||||
@echo ""
|
||||
@echo "$(GREEN)Environment:$(NC)"
|
||||
@echo " PWD: $(PWD)"
|
||||
@echo " SHELL: $(SHELL)"
|
||||
@echo " MAKE: $(MAKE)"
|
||||
@echo ""
|
||||
@echo "$(GREEN)Git status:$(NC)"
|
||||
@git status --short || echo " Not in git repository"
|
||||
@echo ""
|
||||
@echo "$(GREEN)Tools:$(NC)"
|
||||
@echo " pre-commit: $$(which pre-commit || echo 'not found')"
|
||||
@echo " git: $$(which git || echo 'not found')"
|
||||
@echo " nvim: $$(which nvim || echo 'not found')"
|
||||
@echo ""
|
||||
@$(MAKE) --no-print-directory version-check
|
||||
|
||||
# Ensure all targets are PHONY (no file dependencies)
|
||||
.PHONY: _release help check version version-check lint lint-fix format lint-lua lint-shell lint-markdown lint-yaml
|
||||
.PHONY: test test-unit test-integration test-golden test-bin release release-patch release-minor release-major
|
||||
.PHONY: clean install dev-setup ci debug
|
||||
@@ -1,46 +1,305 @@
|
||||
#!/bin/bash
|
||||
# Standalone ShellSpec DSL formatter
|
||||
# Enhanced ShellSpec DSL formatter with HEREDOC and comment support
|
||||
# Matches functionality from the nvim-shellspec plugin
|
||||
|
||||
set -e
|
||||
|
||||
# Default configuration
|
||||
INDENT_SIZE=2
|
||||
USE_SPACES=1
|
||||
INDENT_COMMENTS=1
|
||||
DEBUG=0
|
||||
|
||||
# State constants
|
||||
STATE_NORMAL=1
|
||||
STATE_HEREDOC=2
|
||||
|
||||
# Usage information
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: shellspec-format [OPTIONS] [FILE...]
|
||||
|
||||
Enhanced ShellSpec DSL formatter with HEREDOC preservation and smart comment indentation.
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
-s, --indent-size SIZE Set indentation size (default: 2)
|
||||
-t, --tabs Use tabs instead of spaces
|
||||
-n, --no-comment-indent Don't indent comments
|
||||
-d, --debug Enable debug output
|
||||
-v, --version Show version information
|
||||
|
||||
If no files are specified, reads from stdin and writes to stdout.
|
||||
If files are specified, formats them in place.
|
||||
|
||||
EXAMPLES:
|
||||
shellspec-format < input.spec.sh > output.spec.sh
|
||||
shellspec-format file1.spec.sh file2.spec.sh
|
||||
cat file.spec.sh | shellspec-format --indent-size 4 --tabs
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
version() {
|
||||
echo "shellspec-format 2.0.0"
|
||||
echo "Part of nvim-shellspec plugin"
|
||||
}
|
||||
|
||||
# Debug logging
|
||||
debug_log() {
|
||||
if [[ $DEBUG -eq 1 ]]; then
|
||||
echo "DEBUG: $*" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# Detect HEREDOC start and return delimiter
|
||||
detect_heredoc_start() {
|
||||
local line="$1"
|
||||
local trimmed
|
||||
trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
# Check for various HEREDOC patterns
|
||||
local patterns=(
|
||||
"<<([A-Z_][A-Z0-9_]*)"
|
||||
"<<'([^']*)'"
|
||||
"<<\"([^\"]*)\""
|
||||
"<<-([A-Z_][A-Z0-9_]*)"
|
||||
)
|
||||
|
||||
for pattern in "${patterns[@]}"; do
|
||||
if [[ $trimmed =~ $pattern ]]; then
|
||||
echo "${BASH_REMATCH[1]}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if line ends a HEREDOC
|
||||
is_heredoc_end() {
|
||||
local line="$1"
|
||||
local delimiter="$2"
|
||||
local trimmed
|
||||
trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
[[ -n "$delimiter" && "$trimmed" == "$delimiter" ]]
|
||||
}
|
||||
|
||||
# Check if line is a ShellSpec block keyword
|
||||
is_block_keyword() {
|
||||
local line="$1"
|
||||
local trimmed
|
||||
trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
debug_log "Checking if block keyword: '$trimmed'"
|
||||
|
||||
# Standard block keywords
|
||||
if [[ $trimmed =~ ^(Describe|Context|ExampleGroup|It|Specify|Example)[[:space:]] ]]; then
|
||||
debug_log "Matched standard block keyword: '$trimmed'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Prefixed block keywords (x for skip, f for focus)
|
||||
if [[ $trimmed =~ ^[xf](Describe|Context|ExampleGroup|It|Specify|Example)[[:space:]] ]]; then
|
||||
debug_log "Matched prefixed block keyword: '$trimmed'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Data and Parameters blocks
|
||||
if [[ $trimmed =~ ^(Data|Parameters)[[:space:]]*$ ]]; then
|
||||
debug_log "Matched data/parameters block: '$trimmed'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Hook keywords that create blocks (can be standalone)
|
||||
if [[ $trimmed =~ ^(BeforeEach|AfterEach|BeforeAll|AfterAll|Before|After)[[:space:]]*$ ]]; then
|
||||
debug_log "Matched hook keyword: '$trimmed'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Additional hook keywords (can be standalone)
|
||||
if [[ $trimmed =~ ^(BeforeCall|AfterCall|BeforeRun|AfterRun)[[:space:]]*$ ]]; then
|
||||
debug_log "Matched additional hook keyword: '$trimmed'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
debug_log "Not a block keyword: '$trimmed'"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if line is an End keyword
|
||||
is_end_keyword() {
|
||||
local line="$1"
|
||||
local trimmed
|
||||
trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
[[ $trimmed =~ ^End[[:space:]]*$ ]]
|
||||
}
|
||||
|
||||
# Check if line is a comment
|
||||
is_comment() {
|
||||
local line="$1"
|
||||
local trimmed
|
||||
trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
[[ $trimmed =~ ^# ]]
|
||||
}
|
||||
|
||||
# Generate indentation string
|
||||
make_indent() {
|
||||
local level="$1"
|
||||
local total_indent=$((level * INDENT_SIZE))
|
||||
|
||||
if [[ $USE_SPACES -eq 1 ]]; then
|
||||
printf "%*s" $total_indent ""
|
||||
else
|
||||
printf "%*s" $level "" | tr ' ' '\t'
|
||||
fi
|
||||
}
|
||||
|
||||
# Main formatting function
|
||||
format_shellspec() {
|
||||
local indent=0
|
||||
local indent_level=0
|
||||
local state=$STATE_NORMAL
|
||||
local heredoc_delimiter=""
|
||||
local line
|
||||
|
||||
while IFS= read -r line; do
|
||||
local trimmed
|
||||
trimmed=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
# Skip empty lines and comments
|
||||
if [[ -z "$trimmed" || "$trimmed" =~ ^# ]]; then
|
||||
# Handle empty lines
|
||||
if [[ -z "$trimmed" ]]; then
|
||||
echo "$line"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Decrease indent for End
|
||||
if [[ "$trimmed" =~ ^End[[:space:]]*$ ]]; then
|
||||
((indent > 0)) && ((indent--))
|
||||
fi
|
||||
# State machine for HEREDOC handling
|
||||
case $state in
|
||||
"$STATE_NORMAL")
|
||||
# Check for HEREDOC start
|
||||
if heredoc_delimiter=$(detect_heredoc_start "$line"); then
|
||||
state=$STATE_HEREDOC
|
||||
debug_log "HEREDOC start detected: '$heredoc_delimiter'"
|
||||
# Apply current indentation to HEREDOC start line
|
||||
printf "%s%s\n" "$(make_indent $indent_level)" "$trimmed"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Apply indentation
|
||||
printf "%*s%s\n" $((indent * 2)) "" "$trimmed"
|
||||
# Handle End keyword (decrease indent first)
|
||||
if is_end_keyword "$line"; then
|
||||
((indent_level > 0)) && ((indent_level--))
|
||||
printf "%s%s\n" "$(make_indent $indent_level)" "$trimmed"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Increase indent after block keywords
|
||||
if [[ "$trimmed" =~ ^(Describe|Context|ExampleGroup|It|Specify|Example) ]] ||
|
||||
[[ "$trimmed" =~ ^[xf](Describe|Context|ExampleGroup|It|Specify|Example) ]] ||
|
||||
[[ "$trimmed" =~ ^(Data|Parameters)[[:space:]]*$ ]]; then
|
||||
((indent++))
|
||||
fi
|
||||
# Handle comments
|
||||
if is_comment "$line"; then
|
||||
if [[ $INDENT_COMMENTS -eq 1 ]]; then
|
||||
printf "%s%s\n" "$(make_indent $indent_level)" "$trimmed"
|
||||
else
|
||||
# Preserve original comment formatting
|
||||
echo "$line"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
# Handle non-comment lines (ShellSpec commands, etc.)
|
||||
printf "%s%s\n" "$(make_indent $indent_level)" "$trimmed"
|
||||
|
||||
# Increase indent after block keywords
|
||||
if is_block_keyword "$line"; then
|
||||
((indent_level++))
|
||||
debug_log "Block keyword detected: '$trimmed', new indent: $indent_level"
|
||||
fi
|
||||
;;
|
||||
|
||||
"$STATE_HEREDOC")
|
||||
# Check for HEREDOC end
|
||||
if is_heredoc_end "$line" "$heredoc_delimiter"; then
|
||||
state=$STATE_NORMAL
|
||||
debug_log "HEREDOC end detected: '$heredoc_delimiter'"
|
||||
# Apply current indentation to HEREDOC end line
|
||||
printf "%s%s\n" "$(make_indent $indent_level)" "$trimmed"
|
||||
heredoc_delimiter=""
|
||||
else
|
||||
# Preserve original indentation within HEREDOC
|
||||
echo "$line"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Parse command line options
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-v | --version)
|
||||
version
|
||||
exit 0
|
||||
;;
|
||||
-s | --indent-size)
|
||||
INDENT_SIZE="$2"
|
||||
if ! [[ $INDENT_SIZE =~ ^[0-9]+$ ]] || [[ $INDENT_SIZE -lt 1 ]]; then
|
||||
echo "Error: indent-size must be a positive integer" >&2
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
-t | --tabs)
|
||||
USE_SPACES=0
|
||||
shift
|
||||
;;
|
||||
-n | --no-comment-indent)
|
||||
INDENT_COMMENTS=0
|
||||
shift
|
||||
;;
|
||||
-d | --debug)
|
||||
DEBUG=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
echo "Error: Unknown option $1" >&2
|
||||
echo "Use --help for usage information" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Main execution
|
||||
if [[ $# -eq 0 ]]; then
|
||||
# Read from stdin, write to stdout
|
||||
debug_log "Reading from stdin"
|
||||
format_shellspec
|
||||
else
|
||||
# Process files in place
|
||||
for file in "$@"; do
|
||||
if [[ -f "$file" ]]; then
|
||||
format_shellspec <"$file" >"${file}.tmp" && mv "${file}.tmp" "$file"
|
||||
debug_log "Processing file: $file"
|
||||
temp_file=$(mktemp)
|
||||
if format_shellspec <"$file" >"$temp_file"; then
|
||||
mv "$temp_file" "$file"
|
||||
debug_log "Successfully formatted: $file"
|
||||
else
|
||||
rm -f "$temp_file"
|
||||
echo "Error: Failed to format $file" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Error: File not found: $file" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -8,6 +8,9 @@ if exists('g:loaded_shellspec')
|
||||
endif
|
||||
let g:loaded_shellspec = 1
|
||||
|
||||
" Version information
|
||||
let g:shellspec_version = '2.0.0'
|
||||
|
||||
" Detect Neovim and use appropriate implementation
|
||||
if has('nvim-0.7')
|
||||
" Use modern Neovim Lua implementation
|
||||
|
||||
323
tests/bin_format_spec.sh
Executable file
323
tests/bin_format_spec.sh
Executable file
@@ -0,0 +1,323 @@
|
||||
#!/bin/bash
|
||||
# Unit tests for bin/shellspec-format standalone formatter
|
||||
# Tests the CLI formatter against the same test cases used for Lua implementation
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test counters
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
# Get the script directory and project root
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
FORMATTER="$PROJECT_ROOT/bin/shellspec-format"
|
||||
|
||||
# Helper functions
|
||||
print_test() {
|
||||
echo -e "${YELLOW}[BIN-TEST]${NC} $1"
|
||||
# Force flush
|
||||
exec 1>&1
|
||||
}
|
||||
|
||||
print_pass() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
((TESTS_PASSED++))
|
||||
# Force flush
|
||||
exec 1>&1
|
||||
}
|
||||
|
||||
print_fail() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
((TESTS_FAILED++))
|
||||
# Force flush
|
||||
exec 1>&1
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
echo ""
|
||||
echo "Standalone Formatter Test Results:"
|
||||
echo " Passed: $TESTS_PASSED"
|
||||
echo " Failed: $TESTS_FAILED"
|
||||
echo " Total: $((TESTS_PASSED + TESTS_FAILED))"
|
||||
|
||||
if [ $TESTS_FAILED -gt 0 ]; then
|
||||
echo -e "${RED}Some standalone formatter tests failed!${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}All standalone formatter tests passed!${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to run a formatting test
|
||||
run_format_test() {
|
||||
local test_name="$1"
|
||||
local input_content="$2"
|
||||
local expected_content="$3"
|
||||
|
||||
print_test "Testing $test_name"
|
||||
|
||||
# Create temporary files
|
||||
local input_file
|
||||
local expected_file
|
||||
local actual_file
|
||||
input_file=$(mktemp -t "bin_format_input_XXXXXX.spec.sh")
|
||||
expected_file=$(mktemp -t "bin_format_expected_XXXXXX.spec.sh")
|
||||
actual_file=$(mktemp -t "bin_format_actual_XXXXXX.spec.sh")
|
||||
|
||||
# Debug: Show what we're testing
|
||||
if [[ -n "${DEBUG:-}" ]]; then
|
||||
echo " Input file: $input_file"
|
||||
echo " Expected file: $expected_file"
|
||||
echo " Actual file: $actual_file"
|
||||
fi
|
||||
|
||||
# Write test data to files
|
||||
printf "%s\n" "$input_content" >"$input_file"
|
||||
printf "%s\n" "$expected_content" >"$expected_file"
|
||||
|
||||
# Format using the standalone formatter
|
||||
if timeout 10 "$FORMATTER" <"$input_file" >"$actual_file" 2>/dev/null; then
|
||||
# Compare with expected output
|
||||
if diff -u "$expected_file" "$actual_file" >/dev/null; then
|
||||
print_pass "$test_name formatting matches expected output"
|
||||
else
|
||||
print_fail "$test_name formatting does not match expected output"
|
||||
echo "Expected:"
|
||||
cat "$expected_file"
|
||||
echo ""
|
||||
echo "Actual:"
|
||||
cat "$actual_file"
|
||||
echo ""
|
||||
echo "Diff:"
|
||||
diff -u "$expected_file" "$actual_file" || true
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
print_fail "$test_name formatting command failed"
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
rm -f "$input_file" "$expected_file" "$actual_file"
|
||||
}
|
||||
|
||||
# Function to test CLI options
|
||||
test_cli_options() {
|
||||
local test_name="$1"
|
||||
local options="$2"
|
||||
local input_content="$3"
|
||||
local expected_content="$4"
|
||||
|
||||
print_test "Testing $test_name"
|
||||
|
||||
# Create temporary files
|
||||
local input_file
|
||||
local expected_file
|
||||
local actual_file
|
||||
input_file=$(mktemp -t "bin_format_cli_input_XXXXXX.spec.sh")
|
||||
expected_file=$(mktemp -t "bin_format_cli_expected_XXXXXX.spec.sh")
|
||||
actual_file=$(mktemp -t "bin_format_cli_actual_XXXXXX.spec.sh")
|
||||
|
||||
# Write test data to files
|
||||
printf "%s\n" "$input_content" >"$input_file"
|
||||
printf "%s\n" "$expected_content" >"$expected_file"
|
||||
|
||||
# Format using the standalone formatter with options
|
||||
if timeout 10 bash -c "$FORMATTER $options < '$input_file' > '$actual_file'" 2>/dev/null; then
|
||||
# Compare with expected output
|
||||
if diff -u "$expected_file" "$actual_file" >/dev/null; then
|
||||
print_pass "$test_name formatting with options matches expected output"
|
||||
else
|
||||
print_fail "$test_name formatting with options does not match expected output"
|
||||
echo "Options: $options"
|
||||
echo "Expected:"
|
||||
cat "$expected_file"
|
||||
echo ""
|
||||
echo "Actual:"
|
||||
cat "$actual_file"
|
||||
echo ""
|
||||
echo "Diff:"
|
||||
diff -u "$expected_file" "$actual_file" || true
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
print_fail "$test_name formatting command with options failed"
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
rm -f "$input_file" "$expected_file" "$actual_file"
|
||||
}
|
||||
|
||||
echo "Running bin/shellspec-format standalone formatter tests..."
|
||||
echo "Project root: $PROJECT_ROOT"
|
||||
echo "Formatter: $FORMATTER"
|
||||
echo ""
|
||||
|
||||
# Verify formatter exists and is executable
|
||||
if [[ ! -x "$FORMATTER" ]]; then
|
||||
echo -e "${RED}Error: Formatter not found or not executable: $FORMATTER${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 1: Basic block indentation (ported from format_spec.lua)
|
||||
input1='Describe "test"
|
||||
It "should work"
|
||||
End
|
||||
End'
|
||||
expected1='Describe "test"
|
||||
It "should work"
|
||||
End
|
||||
End'
|
||||
run_format_test "Basic block indentation" "$input1" "$expected1"
|
||||
|
||||
# Test 2: Comment indentation (ported from format_spec.lua)
|
||||
input2='Describe "test"
|
||||
# Comment at Describe level
|
||||
It "should work"
|
||||
# Comment at It level
|
||||
When call echo "test"
|
||||
End
|
||||
End'
|
||||
expected2='Describe "test"
|
||||
# Comment at Describe level
|
||||
It "should work"
|
||||
# Comment at It level
|
||||
When call echo "test"
|
||||
End
|
||||
End'
|
||||
run_format_test "Comment indentation" "$input2" "$expected2"
|
||||
|
||||
# Test 3: HEREDOC preservation (ported from format_spec.lua)
|
||||
run_format_test \
|
||||
"HEREDOC preservation" \
|
||||
'Describe "test"
|
||||
It "handles heredoc"
|
||||
When call cat <<EOF
|
||||
This should be preserved
|
||||
Even nested
|
||||
EOF
|
||||
The output should include "test"
|
||||
End
|
||||
End' \
|
||||
'Describe "test"
|
||||
It "handles heredoc"
|
||||
When call cat <<EOF
|
||||
This should be preserved
|
||||
Even nested
|
||||
EOF
|
||||
The output should include "test"
|
||||
End
|
||||
End'
|
||||
|
||||
# Test 4: Nested contexts (ported from format_spec.lua)
|
||||
run_format_test \
|
||||
"Nested contexts" \
|
||||
'Describe "outer"
|
||||
Context "when something"
|
||||
It "should work"
|
||||
When call echo "test"
|
||||
The output should equal "test"
|
||||
End
|
||||
End
|
||||
End' \
|
||||
'Describe "outer"
|
||||
Context "when something"
|
||||
It "should work"
|
||||
When call echo "test"
|
||||
The output should equal "test"
|
||||
End
|
||||
End
|
||||
End'
|
||||
|
||||
# Test 5: Hook keywords (ported from format_spec.lua)
|
||||
run_format_test \
|
||||
"Hook keywords" \
|
||||
'Describe "test"
|
||||
BeforeEach
|
||||
setup_test
|
||||
End
|
||||
It "works"
|
||||
When call test_function
|
||||
End
|
||||
End' \
|
||||
'Describe "test"
|
||||
BeforeEach
|
||||
setup_test
|
||||
End
|
||||
It "works"
|
||||
When call test_function
|
||||
End
|
||||
End'
|
||||
|
||||
# CLI-specific tests
|
||||
|
||||
# Test 6: Custom indent size
|
||||
test_cli_options \
|
||||
"Custom indent size (4 spaces)" \
|
||||
"--indent-size 4" \
|
||||
'Describe "test"
|
||||
It "should work"
|
||||
End
|
||||
End' \
|
||||
'Describe "test"
|
||||
It "should work"
|
||||
End
|
||||
End'
|
||||
|
||||
# Test 7: Tab indentation
|
||||
input7='Describe "test"
|
||||
It "should work"
|
||||
End
|
||||
End'
|
||||
expected7='Describe "test"'$'\n\t''It "should work"'$'\n\t''End'$'\n''End'
|
||||
test_cli_options "Tab indentation" "--tabs" "$input7" "$expected7"
|
||||
|
||||
# Test 8: No comment indentation
|
||||
test_cli_options \
|
||||
"No comment indentation" \
|
||||
"--no-comment-indent" \
|
||||
'Describe "test"
|
||||
# Top level comment
|
||||
It "should work"
|
||||
# Nested comment
|
||||
End
|
||||
End' \
|
||||
'Describe "test"
|
||||
# Top level comment
|
||||
It "should work"
|
||||
# Nested comment
|
||||
End
|
||||
End'
|
||||
|
||||
# Test 9: Complex combination - tabs with custom indent size
|
||||
input9='Describe "test"
|
||||
Context "nested"
|
||||
It "should work"
|
||||
End
|
||||
End
|
||||
End'
|
||||
expected9='Describe "test"'$'\n\t''Context "nested"'$'\n\t\t''It "should work"'$'\n\t\t''End'$'\n\t''End'$'\n''End'
|
||||
test_cli_options "Tabs with custom indent size" "--tabs --indent-size 1" "$input9" "$expected9"
|
||||
|
||||
# Test error handling
|
||||
print_test "Testing error handling - invalid indent size"
|
||||
if timeout 5 echo 'test' | "$FORMATTER" --indent-size 0 >/dev/null 2>&1; then
|
||||
print_fail "Should have failed with invalid indent size"
|
||||
else
|
||||
print_pass "Correctly rejected invalid indent size"
|
||||
fi
|
||||
|
||||
print_test "Testing error handling - unknown option"
|
||||
if timeout 5 echo 'test' | "$FORMATTER" --unknown-option >/dev/null 2>&1; then
|
||||
print_fail "Should have failed with unknown option"
|
||||
else
|
||||
print_pass "Correctly rejected unknown option"
|
||||
fi
|
||||
|
||||
print_summary
|
||||
@@ -15,6 +15,7 @@ NC='\033[0m' # No Color
|
||||
UNIT_PASSED=false
|
||||
INTEGRATION_PASSED=false
|
||||
GOLDEN_PASSED=false
|
||||
BIN_FORMAT_PASSED=false
|
||||
|
||||
# Get the script directory and project root
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -110,6 +111,9 @@ echo ""
|
||||
echo -e "${BLUE}----------------------------------------${NC}"
|
||||
echo ""
|
||||
|
||||
# Run bin formatter tests
|
||||
run_test_suite "Standalone Formatter Tests" "script" "./tests/bin_format_spec.sh" BIN_FORMAT_PASSED
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Test Results Summary ${NC}"
|
||||
@@ -134,10 +138,16 @@ else
|
||||
echo -e "${RED}✗ Golden Master Tests: FAILED${NC}"
|
||||
fi
|
||||
|
||||
if [ "$BIN_FORMAT_PASSED" = true ]; then
|
||||
echo -e "${GREEN}✓ Standalone Formatter Tests: PASSED${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Standalone Formatter Tests: FAILED${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Overall result
|
||||
if [ "$UNIT_PASSED" = true ] && [ "$INTEGRATION_PASSED" = true ] && [ "$GOLDEN_PASSED" = true ]; then
|
||||
if [ "$UNIT_PASSED" = true ] && [ "$INTEGRATION_PASSED" = true ] && [ "$GOLDEN_PASSED" = true ] && [ "$BIN_FORMAT_PASSED" = true ]; then
|
||||
echo -e "${GREEN}🎉 ALL TESTS COMPLETED SUCCESSFULLY! 🎉${NC}"
|
||||
echo ""
|
||||
echo -e "${GREEN}The nvim-shellspec plugin is ready for use!${NC}"
|
||||
|
||||
Reference in New Issue
Block a user