476 lines
12 KiB
Bash
476 lines
12 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# CI-WEB.SH - Angular Web Testing Utilities
|
|
# =============================================================================
|
|
# Functions for running Angular/Web frontend tests locally.
|
|
#
|
|
# Test Types:
|
|
# - Unit Tests (Karma/Jasmine)
|
|
# - E2E Tests (Playwright)
|
|
# - Accessibility Tests (Axe-core)
|
|
# - Lighthouse Audits
|
|
# - Storybook Build
|
|
#
|
|
# =============================================================================
|
|
|
|
# Prevent direct execution
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
echo "This script should be sourced, not executed directly."
|
|
exit 1
|
|
fi
|
|
|
|
# =============================================================================
|
|
# CONSTANTS
|
|
# =============================================================================
|
|
|
|
WEB_DIR="${REPO_ROOT:-$(git rev-parse --show-toplevel)}/src/Web/StellaOps.Web"
|
|
WEB_NODE_VERSION="20"
|
|
|
|
# Test categories for Web
|
|
WEB_TEST_CATEGORIES=(
|
|
"web:unit" # Karma unit tests
|
|
"web:e2e" # Playwright E2E
|
|
"web:a11y" # Accessibility
|
|
"web:lighthouse" # Performance/a11y audit
|
|
"web:build" # Production build
|
|
"web:storybook" # Storybook build
|
|
)
|
|
|
|
# =============================================================================
|
|
# DEPENDENCY CHECKS
|
|
# =============================================================================
|
|
|
|
check_node_version() {
|
|
if ! command -v node &>/dev/null; then
|
|
log_error "Node.js not found"
|
|
log_info "Install Node.js $WEB_NODE_VERSION+: https://nodejs.org"
|
|
return 1
|
|
fi
|
|
|
|
local version
|
|
version=$(node --version | sed 's/v//' | cut -d. -f1)
|
|
if [[ "$version" -lt "$WEB_NODE_VERSION" ]]; then
|
|
log_warn "Node.js version $version is below recommended $WEB_NODE_VERSION"
|
|
else
|
|
log_debug "Node.js version: $(node --version)"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
check_npm() {
|
|
if ! command -v npm &>/dev/null; then
|
|
log_error "npm not found"
|
|
return 1
|
|
fi
|
|
log_debug "npm version: $(npm --version)"
|
|
return 0
|
|
}
|
|
|
|
check_web_dependencies() {
|
|
log_subsection "Checking Web Dependencies"
|
|
|
|
check_node_version || return 1
|
|
check_npm || return 1
|
|
|
|
# Check if node_modules exists
|
|
if [[ ! -d "$WEB_DIR/node_modules" ]]; then
|
|
log_warn "node_modules not found - will install dependencies"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# =============================================================================
|
|
# SETUP
|
|
# =============================================================================
|
|
|
|
install_web_dependencies() {
|
|
log_subsection "Installing Web Dependencies"
|
|
|
|
if [[ ! -d "$WEB_DIR" ]]; then
|
|
log_error "Web directory not found: $WEB_DIR"
|
|
return 1
|
|
fi
|
|
|
|
pushd "$WEB_DIR" > /dev/null || return 1
|
|
|
|
# Check if package-lock.json exists
|
|
if [[ -f "package-lock.json" ]]; then
|
|
log_info "Running npm ci (clean install)..."
|
|
npm ci --prefer-offline --no-audit --no-fund || {
|
|
log_error "npm ci failed"
|
|
popd > /dev/null
|
|
return 1
|
|
}
|
|
else
|
|
log_info "Running npm install..."
|
|
npm install --no-audit --no-fund || {
|
|
log_error "npm install failed"
|
|
popd > /dev/null
|
|
return 1
|
|
}
|
|
fi
|
|
|
|
popd > /dev/null
|
|
log_success "Web dependencies installed"
|
|
return 0
|
|
}
|
|
|
|
ensure_web_dependencies() {
|
|
if [[ ! -d "$WEB_DIR/node_modules" ]]; then
|
|
install_web_dependencies || return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# =============================================================================
|
|
# TEST RUNNERS
|
|
# =============================================================================
|
|
|
|
run_web_unit_tests() {
|
|
log_subsection "Running Web Unit Tests (Karma/Jasmine)"
|
|
|
|
if [[ ! -d "$WEB_DIR" ]]; then
|
|
log_error "Web directory not found: $WEB_DIR"
|
|
return 1
|
|
fi
|
|
|
|
ensure_web_dependencies || return 1
|
|
|
|
pushd "$WEB_DIR" > /dev/null || return 1
|
|
|
|
local start_time
|
|
start_time=$(start_timer)
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
log_info "[DRY-RUN] Would run: npm run test:ci"
|
|
popd > /dev/null
|
|
return 0
|
|
fi
|
|
|
|
# Run tests
|
|
npm run test:ci
|
|
local result=$?
|
|
|
|
stop_timer "$start_time" "Web unit tests"
|
|
popd > /dev/null
|
|
|
|
if [[ $result -eq 0 ]]; then
|
|
log_success "Web unit tests passed"
|
|
else
|
|
log_error "Web unit tests failed"
|
|
fi
|
|
|
|
return $result
|
|
}
|
|
|
|
run_web_e2e_tests() {
|
|
log_subsection "Running Web E2E Tests (Playwright)"
|
|
|
|
if [[ ! -d "$WEB_DIR" ]]; then
|
|
log_error "Web directory not found: $WEB_DIR"
|
|
return 1
|
|
fi
|
|
|
|
ensure_web_dependencies || return 1
|
|
|
|
pushd "$WEB_DIR" > /dev/null || return 1
|
|
|
|
local start_time
|
|
start_time=$(start_timer)
|
|
|
|
# Install Playwright browsers if needed
|
|
if [[ ! -d "$HOME/.cache/ms-playwright" ]] && [[ ! -d "node_modules/.cache/ms-playwright" ]]; then
|
|
log_info "Installing Playwright browsers..."
|
|
npx playwright install --with-deps chromium || {
|
|
log_warn "Playwright browser installation failed - E2E tests may fail"
|
|
}
|
|
fi
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
log_info "[DRY-RUN] Would run: npm run test:e2e"
|
|
popd > /dev/null
|
|
return 0
|
|
fi
|
|
|
|
# Run E2E tests
|
|
npm run test:e2e
|
|
local result=$?
|
|
|
|
stop_timer "$start_time" "Web E2E tests"
|
|
popd > /dev/null
|
|
|
|
if [[ $result -eq 0 ]]; then
|
|
log_success "Web E2E tests passed"
|
|
else
|
|
log_error "Web E2E tests failed"
|
|
fi
|
|
|
|
return $result
|
|
}
|
|
|
|
run_web_a11y_tests() {
|
|
log_subsection "Running Web Accessibility Tests (Axe)"
|
|
|
|
if [[ ! -d "$WEB_DIR" ]]; then
|
|
log_error "Web directory not found: $WEB_DIR"
|
|
return 1
|
|
fi
|
|
|
|
ensure_web_dependencies || return 1
|
|
|
|
pushd "$WEB_DIR" > /dev/null || return 1
|
|
|
|
local start_time
|
|
start_time=$(start_timer)
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
log_info "[DRY-RUN] Would run: npm run test:a11y"
|
|
popd > /dev/null
|
|
return 0
|
|
fi
|
|
|
|
# Run accessibility tests
|
|
npm run test:a11y
|
|
local result=$?
|
|
|
|
stop_timer "$start_time" "Web accessibility tests"
|
|
popd > /dev/null
|
|
|
|
if [[ $result -eq 0 ]]; then
|
|
log_success "Web accessibility tests passed"
|
|
else
|
|
log_warn "Web accessibility tests had issues (non-blocking)"
|
|
fi
|
|
|
|
# A11y tests are non-blocking by default
|
|
return 0
|
|
}
|
|
|
|
run_web_build() {
|
|
log_subsection "Building Web Application"
|
|
|
|
if [[ ! -d "$WEB_DIR" ]]; then
|
|
log_error "Web directory not found: $WEB_DIR"
|
|
return 1
|
|
fi
|
|
|
|
ensure_web_dependencies || return 1
|
|
|
|
pushd "$WEB_DIR" > /dev/null || return 1
|
|
|
|
local start_time
|
|
start_time=$(start_timer)
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
log_info "[DRY-RUN] Would run: npm run build -- --configuration production"
|
|
popd > /dev/null
|
|
return 0
|
|
fi
|
|
|
|
# Build production bundle
|
|
npm run build -- --configuration production --progress=false
|
|
local result=$?
|
|
|
|
stop_timer "$start_time" "Web build"
|
|
popd > /dev/null
|
|
|
|
if [[ $result -eq 0 ]]; then
|
|
log_success "Web build completed"
|
|
|
|
# Check bundle size
|
|
if [[ -d "$WEB_DIR/dist" ]]; then
|
|
local size
|
|
size=$(du -sh "$WEB_DIR/dist" 2>/dev/null | cut -f1)
|
|
log_info "Bundle size: $size"
|
|
fi
|
|
else
|
|
log_error "Web build failed"
|
|
fi
|
|
|
|
return $result
|
|
}
|
|
|
|
run_web_storybook_build() {
|
|
log_subsection "Building Storybook"
|
|
|
|
if [[ ! -d "$WEB_DIR" ]]; then
|
|
log_error "Web directory not found: $WEB_DIR"
|
|
return 1
|
|
fi
|
|
|
|
ensure_web_dependencies || return 1
|
|
|
|
pushd "$WEB_DIR" > /dev/null || return 1
|
|
|
|
local start_time
|
|
start_time=$(start_timer)
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
log_info "[DRY-RUN] Would run: npm run storybook:build"
|
|
popd > /dev/null
|
|
return 0
|
|
fi
|
|
|
|
# Build Storybook
|
|
npm run storybook:build
|
|
local result=$?
|
|
|
|
stop_timer "$start_time" "Storybook build"
|
|
popd > /dev/null
|
|
|
|
if [[ $result -eq 0 ]]; then
|
|
log_success "Storybook build completed"
|
|
else
|
|
log_error "Storybook build failed"
|
|
fi
|
|
|
|
return $result
|
|
}
|
|
|
|
run_web_lighthouse() {
|
|
log_subsection "Running Lighthouse Audit"
|
|
|
|
if [[ ! -d "$WEB_DIR" ]]; then
|
|
log_error "Web directory not found: $WEB_DIR"
|
|
return 1
|
|
fi
|
|
|
|
# Check if lighthouse is available
|
|
if ! command -v lhci &>/dev/null && ! npx lhci --version &>/dev/null 2>&1; then
|
|
log_warn "Lighthouse CI not installed - skipping audit"
|
|
log_info "Install with: npm install -g @lhci/cli"
|
|
return 0
|
|
fi
|
|
|
|
ensure_web_dependencies || return 1
|
|
|
|
# Build first if not already built
|
|
if [[ ! -d "$WEB_DIR/dist" ]]; then
|
|
run_web_build || return 1
|
|
fi
|
|
|
|
pushd "$WEB_DIR" > /dev/null || return 1
|
|
|
|
local start_time
|
|
start_time=$(start_timer)
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
log_info "[DRY-RUN] Would run: lhci autorun"
|
|
popd > /dev/null
|
|
return 0
|
|
fi
|
|
|
|
# Run Lighthouse
|
|
npx lhci autorun \
|
|
--collect.staticDistDir=./dist/stellaops-web/browser \
|
|
--collect.numberOfRuns=1 \
|
|
--upload.target=filesystem \
|
|
--upload.outputDir=./lighthouse-results 2>/dev/null || {
|
|
log_warn "Lighthouse audit had issues"
|
|
}
|
|
|
|
stop_timer "$start_time" "Lighthouse audit"
|
|
popd > /dev/null
|
|
|
|
log_success "Lighthouse audit completed"
|
|
return 0
|
|
}
|
|
|
|
# =============================================================================
|
|
# COMPOSITE RUNNERS
|
|
# =============================================================================
|
|
|
|
run_web_smoke() {
|
|
log_section "Web Smoke Tests"
|
|
log_info "Running quick web validation"
|
|
|
|
local failed=0
|
|
|
|
run_web_build || failed=1
|
|
|
|
if [[ $failed -eq 0 ]]; then
|
|
run_web_unit_tests || failed=1
|
|
fi
|
|
|
|
return $failed
|
|
}
|
|
|
|
run_web_pr_gating() {
|
|
log_section "Web PR-Gating Tests"
|
|
log_info "Running full web PR-gating suite"
|
|
|
|
local failed=0
|
|
local results=()
|
|
|
|
# Build
|
|
run_web_build
|
|
results+=("Build:$?")
|
|
[[ ${results[-1]##*:} -ne 0 ]] && failed=1
|
|
|
|
# Unit tests
|
|
if [[ $failed -eq 0 ]]; then
|
|
run_web_unit_tests
|
|
results+=("Unit:$?")
|
|
[[ ${results[-1]##*:} -ne 0 ]] && failed=1
|
|
fi
|
|
|
|
# E2E tests
|
|
if [[ $failed -eq 0 ]]; then
|
|
run_web_e2e_tests
|
|
results+=("E2E:$?")
|
|
[[ ${results[-1]##*:} -ne 0 ]] && failed=1
|
|
fi
|
|
|
|
# A11y tests (non-blocking)
|
|
run_web_a11y_tests
|
|
results+=("A11y:$?")
|
|
|
|
# Print summary
|
|
log_section "Web Test Results"
|
|
for result in "${results[@]}"; do
|
|
local name="${result%%:*}"
|
|
local status="${result##*:}"
|
|
if [[ "$status" == "0" ]]; then
|
|
print_status "Web $name" "true"
|
|
else
|
|
print_status "Web $name" "false"
|
|
fi
|
|
done
|
|
|
|
return $failed
|
|
}
|
|
|
|
run_web_full() {
|
|
log_section "Full Web Test Suite"
|
|
log_info "Running all web tests including extended categories"
|
|
|
|
local failed=0
|
|
|
|
# PR-gating tests
|
|
run_web_pr_gating || failed=1
|
|
|
|
# Extended tests
|
|
run_web_storybook_build || log_warn "Storybook build failed (non-blocking)"
|
|
run_web_lighthouse || log_warn "Lighthouse audit failed (non-blocking)"
|
|
|
|
return $failed
|
|
}
|
|
|
|
# =============================================================================
|
|
# EXPORTS
|
|
# =============================================================================
|
|
|
|
export -f check_web_dependencies
|
|
export -f install_web_dependencies
|
|
export -f ensure_web_dependencies
|
|
export -f run_web_unit_tests
|
|
export -f run_web_e2e_tests
|
|
export -f run_web_a11y_tests
|
|
export -f run_web_build
|
|
export -f run_web_storybook_build
|
|
export -f run_web_lighthouse
|
|
export -f run_web_smoke
|
|
export -f run_web_pr_gating
|
|
export -f run_web_full
|