Files
langflow/Makefile.frontend
Viktor Avelino 0007f2032d feat: Add a11y regression scan suite (#13663)
* test: add component a11y unit tests (jest-axe)

Cover 14 components in two waves. 16 tests fail by design - each
encodes a known gap from a11y-action-plan (phases 0-3) and flips to
a regression lock once the component fix lands. 23 tests pass as
regression locks for already-correct semantics.

* ci: split a11y unit tests into separate workflow

Main jest CI excludes *.a11y.test.* so known-gap failures don't
block PRs; new a11y-unit-tests.yml runs them as informational check.

* fix(a11y): resolve component gaps flagged by a11y unit tests

Fixes all 16 failing jest-axe gap tests:
- TableHead defaults scope=col
- alert display area gets aria-live region
- app header becomes <header> landmark; bell button labeled
- icon wrapper decorative by default (aria-hidden), ariaLabel opt-in
- Input drops wrapping <label> (name pollution) and hides placeholder span
- password toggle back in tab order with aria-label/aria-pressed
- CheckBoxDiv exposes checkbox role + state
- accordion trigger renders native Radix button
- dialogs focus container on open instead of suppressing autofocus
- full-screen modal exposes dialog role/aria-modal/ariaLabel
- flow list card focusable with keyboard activation

Also wraps test focus calls in act() and mocks the icon loader so
the a11y suite runs without React warnings.

* fix: skip Puppeteer Chrome download in remaining Dockerfiles

frontend and base images still ran bare npm install; puppeteer (via
accessibility-checker, test-only) tried to fetch Chrome and broke the
Docker image build in CI.

* test: extend a11y unit test coverage (jest-axe)

Adds axe + semantics tests for untested primitives (button, alert,
textarea, radio-group, dropdown-menu, tooltip, popover) and two
known-gap tests that fail by design until fixes land:
- copyFieldAreaComponent copy action is a bare div onClick (plan 2.4)
- DialogContentWithouFixed renders an empty fragment (plan 1.6)

* fix: make webhook copy field a real button (a11y)

Copy action was a bare div onClick - no role, name, or keyboard
support (a11y-action-plan 2.4). Drops the no-longer-needed
DialogContentWithouFixed known-gap test.

* feat: update a11y unit tests and improve accessibility semantics in components

* feat: enable pull request trigger for a11y scan workflow

* test: add a11y regression scan suite

* test: make a11y scans non-blocking

* test: discover a11y scan specs

* ci: group a11y workflow checks
2026-06-15 23:49:21 -04:00

290 lines
14 KiB
Makefile

# Frontend-specific Makefile for Langflow
# This file contains all frontend-related targets
# Variables
FRONTEND_DIR = src/frontend
NPM = npm
.PHONY: install_frontend install_frontendci install_frontendc frontend_deps_check build_frontend run_frontend frontend frontendc format_frontend tests_frontend test_frontend_a11y_e2e test_frontend_a11y_e2e_blocking test_frontend_a11y_e2e_update test_frontend test_frontend_watch test_frontend_coverage test_frontend_verbose test_frontend_ci test_frontend_clean test_frontend_file test_frontend_pattern test_frontend_snapshots test_frontend_config test_frontend_bail test_frontend_silent test_frontend_coverage_open help_frontend storybook storybook_build storybook_network
######################
# FRONTEND DEPENDENCIES
######################
install_frontend: ## install the frontend dependencies
@echo 'Installing frontend dependencies'
@cd $(FRONTEND_DIR) && npm install > /dev/null 2>&1
install_frontendci:
@cd $(FRONTEND_DIR) && npm ci > /dev/null 2>&1
install_frontendc:
@cd $(FRONTEND_DIR) && $(call CLEAR_DIRS,node_modules) && rm -f package-lock.json && npm install > /dev/null 2>&1
# Check if frontend dependencies are installed
frontend_deps_check:
@if [ ! -d "$(FRONTEND_DIR)/node_modules" ]; then \
echo "Frontend dependencies not found. Installing..."; \
$(MAKE) install_frontend; \
fi
######################
# FRONTEND BUILD
######################
build_frontend: ## build the frontend static files
@echo '==== Starting frontend build ===='
@echo 'Current directory: $$(pwd)'
@echo 'Checking if $(FRONTEND_DIR) exists...'
@ls -la $(FRONTEND_DIR) || true
@echo 'Building frontend static files...'
@cd $(FRONTEND_DIR) && CI='' npm run build 2>&1 || { echo "\nBuild failed! Error output above ☝️"; exit 1; }
@echo 'Clearing destination directory...'
$(call CLEAR_DIRS,src/backend/base/langflow/frontend)
@echo 'Copying build files...'
@cp -r $(FRONTEND_DIR)/build/. src/backend/base/langflow/frontend
@echo '==== Frontend build complete ===='
######################
# FRONTEND DEVELOPMENT
######################
run_frontend: ## run the frontend
@-kill -9 `lsof -t -i:3000`
@cd $(FRONTEND_DIR) && npm start $(if $(FRONTEND_START_FLAGS),-- $(FRONTEND_START_FLAGS))
frontend: install_frontend ## run the frontend in development mode
make run_frontend
frontendc: install_frontendc
make run_frontend
######################
# FRONTEND CODE QUALITY
######################
format_frontend: ## frontend code formatters
@cd $(FRONTEND_DIR) && npm run format
######################
# FRONTEND E2E TESTS (PLAYWRIGHT)
######################
tests_frontend: ## run frontend tests
ifeq ($(UI), true)
@cd $(FRONTEND_DIR) && npx playwright test --ui --project=chromium
else
@cd $(FRONTEND_DIR) && npx playwright test --project=chromium
endif
test_frontend_a11y_e2e: frontend_deps_check ## run non-blocking Playwright a11y scans and reports
@cd $(FRONTEND_DIR) && SCAN_SPECS="$$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort)" && echo "Specs with a11y scans:" && echo "$$SCAN_SPECS" && test -n "$$SCAN_SPECS" && RUN_A11Y=true RUN_A11Y_ASSERT=false npx playwright test $$SCAN_SPECS --project=chromium --workers=1
test_frontend_a11y_e2e_blocking: frontend_deps_check ## run blocking Playwright a11y scans against baselines
@cd $(FRONTEND_DIR) && SCAN_SPECS="$$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort)" && echo "Specs with a11y scans:" && echo "$$SCAN_SPECS" && test -n "$$SCAN_SPECS" && RUN_A11Y=true RUN_A11Y_ASSERT=true npx playwright test $$SCAN_SPECS --project=chromium --workers=1
test_frontend_a11y_e2e_update: frontend_deps_check ## refresh Playwright a11y reports used as baselines
@cd $(FRONTEND_DIR) && rm -rf coverage/accessibility-reports && SCAN_SPECS="$$(grep -rl "runA11yScan(" tests --include="*.spec.ts" | sort)" && echo "Specs with a11y scans:" && echo "$$SCAN_SPECS" && test -n "$$SCAN_SPECS" && RUN_A11Y=true RUN_A11Y_ASSERT=false npx playwright test $$SCAN_SPECS --project=chromium --workers=1
@mkdir -p $(FRONTEND_DIR)/tests/baselines
@cp $(FRONTEND_DIR)/coverage/accessibility-reports/chromium__*.json $(FRONTEND_DIR)/tests/baselines/
######################
# FRONTEND UNIT TESTS (JEST)
######################
# Run all frontend Jest unit tests
test_frontend: frontend_deps_check ## run all frontend Jest unit tests
@echo "Running all frontend Jest unit tests..."
@cd $(FRONTEND_DIR) && $(NPM) test
# Run frontend tests in watch mode
test_frontend_watch: frontend_deps_check ## run frontend tests in watch mode
@echo "Running frontend tests in watch mode..."
@cd $(FRONTEND_DIR) && $(NPM) run test:watch
# Run frontend tests with coverage report
test_frontend_coverage: frontend_deps_check ## run frontend tests with coverage report
@echo "Running frontend tests with coverage report..."
@cd $(FRONTEND_DIR) && npx jest --coverage
# Run frontend tests with verbose output
test_frontend_verbose: frontend_deps_check ## run frontend tests with verbose output
@echo "Running frontend tests with verbose output..."
@cd $(FRONTEND_DIR) && npx jest --verbose
# Run frontend tests in CI mode (no watch, with coverage)
# Excludes *.a11y.test.* files - those run in their own blocking
# workflow (a11y-unit-tests.yml) with a dedicated junit check.
test_frontend_ci: frontend_deps_check ## run frontend tests in CI mode
@echo "Running frontend tests in CI mode..."
@cd $(FRONTEND_DIR) && CI=true npx jest --ci --coverage --watchAll=false --testPathIgnorePatterns="/node_modules/" "test-utils.tsx" "\.a11y\.test\."
# Run only the a11y component tests (jest-axe)
test_frontend_a11y_ci: frontend_deps_check ## run frontend a11y unit tests in CI mode
@echo "Running frontend a11y unit tests in CI mode..."
@cd $(FRONTEND_DIR) && CI=true npx jest --ci --watchAll=false --testPathPatterns="\.a11y\.test\."
# Clean test cache and run tests
test_frontend_clean: frontend_deps_check ## clean test cache and run tests
@echo "Cleaning Jest cache and running tests..."
@cd $(FRONTEND_DIR) && npx jest --clearCache && npx jest
# Run tests for a specific file
test_frontend_file: frontend_deps_check ## run tests for a specific file (usage: make test_frontend_file path/to/test.ts)
$(eval file := $(word 2,$(MAKECMDGOALS)))
@if [ -z "$(file)" ]; then \
echo "Usage: make test_frontend_file path/to/test.ts"; \
exit 1; \
fi
@echo "Running tests for file: $(file)"
@cd $(FRONTEND_DIR) && npx jest $(file)
# Prevent make from treating the file argument as another target
%:
@:
# Run tests matching a pattern
test_frontend_pattern: frontend_deps_check ## run tests matching a pattern (usage: make test_frontend_pattern pattern)
$(eval pattern := $(word 2,$(MAKECMDGOALS)))
@if [ -z "$(pattern)" ]; then \
echo "Usage: make test_frontend_pattern pattern"; \
exit 1; \
fi
@echo "Running tests matching pattern: $(pattern)"
@cd $(FRONTEND_DIR) && npx jest --testNamePattern="$(pattern)"
# Update test snapshots
test_frontend_snapshots: frontend_deps_check ## update Jest snapshots
@echo "Updating Jest snapshots..."
@cd $(FRONTEND_DIR) && npx jest --updateSnapshot
# Show test configuration
test_frontend_config: ## show Jest configuration
@echo "Jest configuration:"
@cd $(FRONTEND_DIR) && npx jest --showConfig
# Run Jest tests with bail (stop on first failure)
test_frontend_bail: frontend_deps_check ## run tests with bail (stop on first failure)
@echo "Running Jest tests with bail (stop on first failure)..."
@cd $(FRONTEND_DIR) && npx jest --bail
# Run Jest tests silently (minimal output)
test_frontend_silent: frontend_deps_check ## run tests silently (minimal output)
@echo "Running Jest tests silently..."
@cd $(FRONTEND_DIR) && npx jest --silent
# Run Jest tests and open coverage report in browser
test_frontend_coverage_open: test_frontend_coverage ## run tests with coverage and open report in browser
@echo "Opening coverage report in browser..."
@if command -v open >/dev/null 2>&1; then \
open $(FRONTEND_DIR)/coverage/lcov-report/index.html; \
elif command -v xdg-open >/dev/null 2>&1; then \
xdg-open $(FRONTEND_DIR)/coverage/lcov-report/index.html; \
else \
echo "Coverage report generated at: $(FRONTEND_DIR)/coverage/lcov-report/index.html"; \
fi
######################
# FRONTEND TESTS COVERAGE (JEST + PLAYWRIGHT COMBINED )
######################
# Run full frontend tests (Jest + Playwright) with coverage and merge into HTML
test_frontend_coverage_full: frontend_deps_check
@echo "=== Running Jest unit tests with coverage ==="
@(cd $(FRONTEND_DIR) && npx jest --coverage); echo $$? > /tmp/jest_exit
@echo "=== Running Playwright E2E tests ==="
@(cd $(FRONTEND_DIR) && CI=1 npx playwright test --project=chromium); echo $$? > /tmp/playwright_exit
@echo "=== Merging coverage reports ==="
@mkdir -p $(FRONTEND_DIR)/coverage/combined
# Merge Playwright raw coverage files
@cd $(FRONTEND_DIR) && npx nyc merge coverage/playwright/individual-test coverage/combined/playwright.json || echo "Failed to merge Playwright coverage"
# Generate Playwright HTML report
@cd $(FRONTEND_DIR) && npx nyc report --reporter=html --reporter=text --temp-dir coverage/playwright/individual-test --report-dir coverage/playwright/html-report || echo "Failed to generate Playwright HTML coverage"
# Migrate Jest coverage
@cp $(FRONTEND_DIR)/coverage/jest/coverage-final.json $(FRONTEND_DIR)/coverage/combined/jest.json || echo "Failed to migrate Jest coverage"
# Merge both into final
@cd $(FRONTEND_DIR) && npx nyc merge coverage/combined coverage/combined/total-frontend-coverage.json || echo "Failed to combine Playwright + Jest coverage"
@echo "=== Generating HTML coverage report ==="
@cd $(FRONTEND_DIR) && npx nyc report --reporter=html --reporter=text --temp-dir coverage/combined --report-dir coverage/final-coverage-report || echo "Failed to generate Playwright + Jest coverage html report"
@echo "=== Checking test results ==="
@JEST=$$(cat /tmp/jest_exit); \
PLAY=$$(cat /tmp/playwright_exit); \
rm -f /tmp/jest_exit /tmp/playwright_exit; \
test $$JEST -eq 0 -a $$PLAY -eq 0
######################
# STORYBOOK
######################
storybook: frontend_deps_check ## run Storybook development server and open in browser
@echo "Starting Storybook development server on http://localhost:6006..."
@cd $(FRONTEND_DIR) && $(NPM) run storybook
storybook_build: frontend_deps_check ## build static Storybook
@echo "Building static Storybook..."
@cd $(FRONTEND_DIR) && $(NPM) run build-storybook
@echo "Storybook built to $(FRONTEND_DIR)/storybook-static"
storybook_network: frontend_deps_check ## run Storybook accessible on network (0.0.0.0:6006)
@echo "Starting Storybook development server accessible on network..."
@cd $(FRONTEND_DIR) && $(NPM) run storybook:network
######################
# FRONTEND HELP
######################
help_frontend: ## show frontend help
@echo ''
@echo "$(GREEN)═══════════════════════════════════════════════════════════════════$(NC)"
@echo "$(GREEN) FRONTEND COMMANDS $(NC)"
@echo "$(GREEN)═══════════════════════════════════════════════════════════════════$(NC)"
@echo ''
@echo "$(GREEN)Dependencies:$(NC)"
@echo " $(GREEN)make install_frontend$(NC) - Install frontend dependencies"
@echo " $(GREEN)make install_frontendci$(NC) - Install frontend dependencies with npm ci"
@echo " $(GREEN)make install_frontendc$(NC) - Clean install frontend dependencies"
@echo ''
@echo "$(GREEN)Build & Development:$(NC)"
@echo " $(GREEN)make build_frontend$(NC) - Build frontend static files"
@echo " $(GREEN)make run_frontend$(NC) - Run the frontend development server"
@echo " $(GREEN)make frontend$(NC) - Install dependencies and run frontend in dev mode"
@echo " $(GREEN)make frontendc$(NC) - Clean install dependencies and run frontend"
@echo ''
@echo "$(GREEN)Code Quality:$(NC)"
@echo " $(GREEN)make format_frontend$(NC) - Format frontend code"
@echo " $(GREEN)make format_frontend_check$(NC) - Check frontend formatting (biome)"
@echo ''
@echo "$(GREEN)E2E Testing (Playwright):$(NC)"
@echo " $(GREEN)make tests_frontend$(NC) - Run Playwright e2e tests"
@echo " $(GREEN)make tests_frontend UI=true$(NC) - Run Playwright e2e tests with UI"
@echo ''
@echo "$(GREEN)Unit Testing (Jest):$(NC)"
@echo " $(GREEN)make test_frontend$(NC) - Run Jest unit tests"
@echo " $(GREEN)make test_frontend_watch$(NC) - Run unit tests in watch mode"
@echo " $(GREEN)make test_frontend_coverage$(NC) - Run unit tests with coverage"
@echo " $(GREEN)make test_frontend_coverage_open$(NC) - Run coverage and open report"
@echo " $(GREEN)make test_frontend_verbose$(NC) - Run unit tests with verbose output"
@echo " $(GREEN)make test_frontend_ci$(NC) - Run unit tests in CI mode"
@echo " $(GREEN)make test_frontend_clean$(NC) - Clean cache and run unit tests"
@echo " $(GREEN)make test_frontend_bail$(NC) - Run unit tests with bail"
@echo " $(GREEN)make test_frontend_silent$(NC) - Run unit tests silently"
@echo ''
@echo "$(GREEN)Targeted Testing:$(NC)"
@echo " $(GREEN)make test_frontend_file path$(NC) - Run tests for specific file"
@echo " $(GREEN)make test_frontend_pattern pattern$(NC) - Run tests matching pattern"
@echo " $(GREEN)make test_frontend_snapshots$(NC) - Update Jest snapshots"
@echo " $(GREEN)make test_frontend_config$(NC) - Show Jest configuration"
@echo ''
@echo "$(GREEN)Storybook:$(NC)"
@echo " $(GREEN)make storybook$(NC) - Run Storybook dev server and open in browser"
@echo " $(GREEN)make storybook_build$(NC) - Build static Storybook"
@echo " $(GREEN)make storybook_network$(NC) - Run Storybook accessible on network"
@echo ''
@echo "$(GREEN)═══════════════════════════════════════════════════════════════════$(NC)"
@echo ''