diff --git a/.github/workflows/security-comprehensive.yml b/.github/workflows/security-comprehensive.yml index 0e85f7a6..77699d3d 100644 --- a/.github/workflows/security-comprehensive.yml +++ b/.github/workflows/security-comprehensive.yml @@ -271,26 +271,8 @@ jobs: run: | python scripts/aio-version-checker.py \ --iac-type ${{ inputs.iac-types }} \ - --output-format json \ - --output-path aio-version-check-results.json - - # Parse results for outputs - if [[ -f "aio-version-check-results.json" ]]; then - issues=$(jq '.issues | length' aio-version-check-results.json 2>/dev/null || echo "0") - echo "issues=$issues" >> $GITHUB_OUTPUT - echo "AIO version check completed with $issues issues" - else - echo "issues=0" >> $GITHUB_OUTPUT - echo "AIO version check completed (no results file)" - fi - - - name: Upload AIO version results - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: aio-version-check-results - path: aio-version-check-results.json - retention-days: 30 + --error-on-mismatch \ + --verbose # Comprehensive dependency pinning analysis security-analysis: diff --git a/.github/workflows/security-deployment.yml b/.github/workflows/security-deployment.yml index 192cf07b..4d2fd4af 100644 --- a/.github/workflows/security-deployment.yml +++ b/.github/workflows/security-deployment.yml @@ -180,16 +180,8 @@ jobs: run: | python scripts/aio-version-checker.py \ --iac-type ${{ inputs.iac-types }} \ - --break-build ${{ inputs.break-build }} \ - --output-format json - - - name: Upload AIO version results - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: aio-version-check-results - path: aio-version-check-results.json - retention-days: 30 + --error-on-mismatch \ + --verbose # Comprehensive security validation summary security-validation-summary: diff --git a/.github/workflows/shell-lint.yml b/.github/workflows/shell-lint.yml index b7d790be..eb0bf678 100644 --- a/.github/workflows/shell-lint.yml +++ b/.github/workflows/shell-lint.yml @@ -79,7 +79,7 @@ jobs: id: shellcheck run: | find . -name '*.sh' -not -path './node_modules/*' -not -path './.copilot-tracking/*' -print0 \ - | xargs -0 -r shellcheck --rcfile .shellcheckrc --format=gcc > shellcheck-output.txt 2>&1 || echo "SHELLCHECK_FAILED=true" >> "$GITHUB_ENV" + | xargs -0 -r shellcheck --format=gcc > shellcheck-output.txt 2>&1 || echo "SHELLCHECK_FAILED=true" >> "$GITHUB_ENV" cat shellcheck-output.txt continue-on-error: true diff --git a/.github/workflows/terraform-lint.yml b/.github/workflows/terraform-lint.yml index b9b578ea..dd303571 100644 --- a/.github/workflows/terraform-lint.yml +++ b/.github/workflows/terraform-lint.yml @@ -73,8 +73,8 @@ jobs: - name: TFLint init and check id: tflint run: | - tflint --init - tflint --recursive > tflint-output.txt 2>&1 || echo "TFLINT_FAILED=true" >> "$GITHUB_ENV" + tflint --init --config "$(pwd)/.tflint.hcl" + tflint --recursive --config "$(pwd)/.tflint.hcl" > tflint-output.txt 2>&1 || echo "TFLINT_FAILED=true" >> "$GITHUB_ENV" cat tflint-output.txt continue-on-error: true diff --git a/blueprints/full-single-node-cluster/tests/run-contract-tests.sh b/blueprints/full-single-node-cluster/tests/run-contract-tests.sh index 1d592400..5c888de2 100755 --- a/blueprints/full-single-node-cluster/tests/run-contract-tests.sh +++ b/blueprints/full-single-node-cluster/tests/run-contract-tests.sh @@ -15,7 +15,7 @@ BLUE='\033[0;34m' NC='\033[0m' # No Color print_usage() { - cat << EOF + cat < /dev/null; then - echo -e "${RED}✗ Go not found. Please install Go toolchain.${NC}" - exit 1 +if ! command -v go &>/dev/null; then + echo -e "${RED}✗ Go not found. Please install Go toolchain.${NC}" + exit 1 fi echo -e "${GREEN}✓ Go: $(go version | awk '{print $3}')${NC}" # Check terraform-docs if [[ "$TEST_TYPE" == "terraform" || "$TEST_TYPE" == "both" ]]; then - if ! command -v terraform-docs &> /dev/null; then - echo -e "${RED}✗ terraform-docs not found${NC}" - echo -e "${YELLOW} Install: brew install terraform-docs${NC}" - exit 1 - fi - echo -e "${GREEN}✓ terraform-docs: $(terraform-docs version | head -n1)${NC}" + if ! command -v terraform-docs &>/dev/null; then + echo -e "${RED}✗ terraform-docs not found${NC}" + echo -e "${YELLOW} Install: brew install terraform-docs${NC}" + exit 1 + fi + echo -e "${GREEN}✓ terraform-docs: $(terraform-docs version | head -n1)${NC}" fi # Check az bicep if [[ "$TEST_TYPE" == "bicep" || "$TEST_TYPE" == "both" ]]; then - if ! command -v az &> /dev/null; then - echo -e "${RED}✗ Azure CLI not found${NC}" - echo -e "${YELLOW} Install: https://docs.microsoft.com/cli/azure/install-azure-cli${NC}" - exit 1 - fi - - # Check bicep is installed - if ! az bicep version &> /dev/null; then - echo -e "${RED}✗ Bicep not installed${NC}" - echo -e "${YELLOW} Install: az bicep install${NC}" - exit 1 - fi + if ! command -v az &>/dev/null; then + echo -e "${RED}✗ Azure CLI not found${NC}" + echo -e "${YELLOW} Install: https://docs.microsoft.com/cli/azure/install-azure-cli${NC}" + exit 1 + fi + + # Check bicep is installed + if ! az bicep version &>/dev/null; then + echo -e "${RED}✗ Bicep not installed${NC}" + echo -e "${YELLOW} Install: az bicep install${NC}" + exit 1 + fi fi echo "" @@ -124,41 +124,41 @@ echo "" EXIT_CODE=0 run_test() { - local test_name=$1 - local test_pattern=$2 - - echo -e "${BLUE}──────────────────────────────────────────────────────────${NC}" - echo -e "${YELLOW}Running: $test_name${NC}" - echo -e "${BLUE}──────────────────────────────────────────────────────────${NC}" - - if go test $VERBOSE_FLAG -run "$test_pattern" .; then - echo -e "${GREEN}✓ $test_name PASSED${NC}" - else - echo -e "${RED}✗ $test_name FAILED${NC}" - EXIT_CODE=1 - fi - echo "" + local test_name=$1 + local test_pattern=$2 + + echo -e "${BLUE}──────────────────────────────────────────────────────────${NC}" + echo -e "${YELLOW}Running: $test_name${NC}" + echo -e "${BLUE}──────────────────────────────────────────────────────────${NC}" + + if go test $VERBOSE_FLAG -run "$test_pattern" .; then + echo -e "${GREEN}✓ $test_name PASSED${NC}" + else + echo -e "${RED}✗ $test_name FAILED${NC}" + EXIT_CODE=1 + fi + echo "" } case $TEST_TYPE in - terraform) - run_test "Terraform Contract Test" "TestTerraformOutputsContract" - ;; - bicep) - run_test "Bicep Contract Test" "TestBicepOutputsContract" - ;; - both) - run_test "Terraform Contract Test" "TestTerraformOutputsContract" - run_test "Bicep Contract Test" "TestBicepOutputsContract" - ;; + terraform) + run_test "Terraform Contract Test" "TestTerraformOutputsContract" + ;; + bicep) + run_test "Bicep Contract Test" "TestBicepOutputsContract" + ;; + both) + run_test "Terraform Contract Test" "TestTerraformOutputsContract" + run_test "Bicep Contract Test" "TestBicepOutputsContract" + ;; esac # Summary echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" if [[ $EXIT_CODE -eq 0 ]]; then - echo -e "${BLUE}║${GREEN} All Tests PASSED ✓ ${BLUE}║${NC}" + echo -e "${BLUE}║${GREEN} All Tests PASSED ✓ ${BLUE}║${NC}" else - echo -e "${BLUE}║${RED} Some Tests FAILED ✗ ${BLUE}║${NC}" + echo -e "${BLUE}║${RED} Some Tests FAILED ✗ ${BLUE}║${NC}" fi echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" diff --git a/blueprints/full-single-node-cluster/tests/run-deployment-tests.sh b/blueprints/full-single-node-cluster/tests/run-deployment-tests.sh index 95d6e856..2f5868fe 100755 --- a/blueprints/full-single-node-cluster/tests/run-deployment-tests.sh +++ b/blueprints/full-single-node-cluster/tests/run-deployment-tests.sh @@ -13,26 +13,26 @@ YELLOW='\033[1;33m' NC='\033[0m' # No Color print_usage() { - echo "Usage: $0 [terraform|bicep|both] [options]" - echo "" - echo "Arguments:" - echo " terraform Run only Terraform deployment tests" - echo " bicep Run only Bicep deployment tests" - echo " both Run both Terraform and Bicep tests (default)" - echo "" - echo "Options:" - echo " -v, --verbose Enable verbose test output" - echo " -h, --help Show this help message" - echo "" - echo "Environment Variables:" - echo " ARM_SUBSCRIPTION_ID Azure subscription ID (auto-detected if not set)" - echo " ADMIN_PASSWORD (Required for Bicep) VM admin password" - echo " CUSTOM_LOCATIONS_OID Custom Locations OID (auto-detected if not set)" - echo "" - echo "Examples:" - echo " $0 terraform" - echo " $0 bicep -v" - echo " $0 both" + echo "Usage: $0 [terraform|bicep|both] [options]" + echo "" + echo "Arguments:" + echo " terraform Run only Terraform deployment tests" + echo " bicep Run only Bicep deployment tests" + echo " both Run both Terraform and Bicep tests (default)" + echo "" + echo "Options:" + echo " -v, --verbose Enable verbose test output" + echo " -h, --help Show this help message" + echo "" + echo "Environment Variables:" + echo " ARM_SUBSCRIPTION_ID Azure subscription ID (auto-detected if not set)" + echo " ADMIN_PASSWORD (Required for Bicep) VM admin password" + echo " CUSTOM_LOCATIONS_OID Custom Locations OID (auto-detected if not set)" + echo "" + echo "Examples:" + echo " $0 terraform" + echo " $0 bicep -v" + echo " $0 both" } # Parse arguments @@ -40,59 +40,59 @@ DEPLOYMENT_TYPE="both" VERBOSE_FLAG="" while [[ $# -gt 0 ]]; do - case $1 in - terraform|bicep|both) - DEPLOYMENT_TYPE="$1" - shift - ;; - -v|--verbose) - VERBOSE_FLAG="-v" - shift - ;; - -h|--help) - print_usage - exit 0 - ;; - *) - echo -e "${RED}Unknown option: $1${NC}" - print_usage - exit 1 - ;; - esac + case $1 in + terraform | bicep | both) + DEPLOYMENT_TYPE="$1" + shift + ;; + -v | --verbose) + VERBOSE_FLAG="-v" + shift + ;; + -h | --help) + print_usage + exit 0 + ;; + *) + echo -e "${RED}Unknown option: $1${NC}" + print_usage + exit 1 + ;; + esac done # Auto-detect ARM_SUBSCRIPTION_ID if not set if [[ -z "${ARM_SUBSCRIPTION_ID}" ]]; then - echo -e "${YELLOW}ARM_SUBSCRIPTION_ID not set, detecting from Azure CLI...${NC}" - ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv 2>/dev/null) - if [[ -z "${ARM_SUBSCRIPTION_ID}" ]]; then - echo -e "${RED}Error: Could not auto-detect ARM_SUBSCRIPTION_ID. Please run 'az login' or set ARM_SUBSCRIPTION_ID${NC}" - exit 1 - fi - echo -e "${GREEN}Detected subscription: ${ARM_SUBSCRIPTION_ID}${NC}" - export ARM_SUBSCRIPTION_ID + echo -e "${YELLOW}ARM_SUBSCRIPTION_ID not set, detecting from Azure CLI...${NC}" + ARM_SUBSCRIPTION_ID=$(az account show --query id -o tsv 2>/dev/null) + if [[ -z "${ARM_SUBSCRIPTION_ID}" ]]; then + echo -e "${RED}Error: Could not auto-detect ARM_SUBSCRIPTION_ID. Please run 'az login' or set ARM_SUBSCRIPTION_ID${NC}" + exit 1 + fi + echo -e "${GREEN}Detected subscription: ${ARM_SUBSCRIPTION_ID}${NC}" + export ARM_SUBSCRIPTION_ID fi # Auto-detect CUSTOM_LOCATIONS_OID if not set (for Bicep tests) if [[ -z "${CUSTOM_LOCATIONS_OID}" ]] && [[ "$DEPLOYMENT_TYPE" == "bicep" || "$DEPLOYMENT_TYPE" == "both" ]]; then - echo -e "${YELLOW}CUSTOM_LOCATIONS_OID not set, detecting from Azure AD...${NC}" - CUSTOM_LOCATIONS_OID=$(az ad sp show --id bc313c14-388c-4e7d-a58e-70017303ee3b --query id -o tsv 2>/dev/null) - if [[ -z "${CUSTOM_LOCATIONS_OID}" ]]; then - echo -e "${RED}Error: Could not auto-detect CUSTOM_LOCATIONS_OID. Please ensure you have permissions to query Azure AD${NC}" - exit 1 - fi - echo -e "${GREEN}Detected Custom Locations OID: ${CUSTOM_LOCATIONS_OID}${NC}" - export CUSTOM_LOCATIONS_OID + echo -e "${YELLOW}CUSTOM_LOCATIONS_OID not set, detecting from Azure AD...${NC}" + CUSTOM_LOCATIONS_OID=$(az ad sp show --id bc313c14-388c-4e7d-a58e-70017303ee3b --query id -o tsv 2>/dev/null) + if [[ -z "${CUSTOM_LOCATIONS_OID}" ]]; then + echo -e "${RED}Error: Could not auto-detect CUSTOM_LOCATIONS_OID. Please ensure you have permissions to query Azure AD${NC}" + exit 1 + fi + echo -e "${GREEN}Detected Custom Locations OID: ${CUSTOM_LOCATIONS_OID}${NC}" + export CUSTOM_LOCATIONS_OID fi # Generate strong admin password if not provided (for Bicep tests) if [[ -z "${ADMIN_PASSWORD}" ]] && [[ "$DEPLOYMENT_TYPE" == "bicep" || "$DEPLOYMENT_TYPE" == "both" ]]; then - echo -e "${YELLOW}ADMIN_PASSWORD not set, generating strong password...${NC}" - ADMIN_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-24) - # Ensure password meets Azure complexity requirements (uppercase, lowercase, digit, special char) - ADMIN_PASSWORD="Aa1!${ADMIN_PASSWORD}" - echo -e "${GREEN}Generated admin password (save this): ${ADMIN_PASSWORD}${NC}" - export ADMIN_PASSWORD + echo -e "${YELLOW}ADMIN_PASSWORD not set, generating strong password...${NC}" + ADMIN_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-24) + # Ensure password meets Azure complexity requirements (uppercase, lowercase, digit, special char) + ADMIN_PASSWORD="Aa1!${ADMIN_PASSWORD}" + echo -e "${GREEN}Generated admin password (save this): ${ADMIN_PASSWORD}${NC}" + export ADMIN_PASSWORD fi echo -e "${GREEN}=== Deployment Tests ===${NC}" @@ -109,55 +109,55 @@ echo "Location: ${TEST_LOCATION}" echo "" run_terraform_tests() { - export TEST_RESOURCE_GROUP_NAME="${TEST_RESOURCE_GROUP_NAME_PREFIX:-test-}terraform" - echo "Resource Group: ${TEST_RESOURCE_GROUP_NAME}" - - echo -e "${YELLOW}Running Terraform deployment tests...${NC}" - if go test $VERBOSE_FLAG -run TestTerraformFullSingleNodeClusterDeploy -timeout 2h; then - echo -e "${GREEN}✓ Terraform tests passed${NC}" - return 0 - else - echo -e "${RED}✗ Terraform tests failed${NC}" - return 1 - fi + export TEST_RESOURCE_GROUP_NAME="${TEST_RESOURCE_GROUP_NAME_PREFIX:-test-}terraform" + echo "Resource Group: ${TEST_RESOURCE_GROUP_NAME}" + + echo -e "${YELLOW}Running Terraform deployment tests...${NC}" + if go test $VERBOSE_FLAG -run TestTerraformFullSingleNodeClusterDeploy -timeout 2h; then + echo -e "${GREEN}✓ Terraform tests passed${NC}" + return 0 + else + echo -e "${RED}✗ Terraform tests failed${NC}" + return 1 + fi } run_bicep_tests() { - export TEST_RESOURCE_GROUP_NAME="${TEST_RESOURCE_GROUP_NAME_PREFIX:-test-}bicep" - echo "Resource Group: ${TEST_RESOURCE_GROUP_NAME}" - - echo -e "${YELLOW}Running Bicep deployment tests...${NC}" - if go test $VERBOSE_FLAG -run TestBicepFullSingleNodeClusterDeploy -timeout 2h; then - echo -e "${GREEN}✓ Bicep tests passed${NC}" - return 0 - else - echo -e "${RED}✗ Bicep tests failed${NC}" - return 1 - fi + export TEST_RESOURCE_GROUP_NAME="${TEST_RESOURCE_GROUP_NAME_PREFIX:-test-}bicep" + echo "Resource Group: ${TEST_RESOURCE_GROUP_NAME}" + + echo -e "${YELLOW}Running Bicep deployment tests...${NC}" + if go test $VERBOSE_FLAG -run TestBicepFullSingleNodeClusterDeploy -timeout 2h; then + echo -e "${GREEN}✓ Bicep tests passed${NC}" + return 0 + else + echo -e "${RED}✗ Bicep tests failed${NC}" + return 1 + fi } # Run tests based on deployment type EXIT_CODE=0 case $DEPLOYMENT_TYPE in - terraform) - run_terraform_tests || EXIT_CODE=$? - ;; - bicep) - run_bicep_tests || EXIT_CODE=$? - ;; - both) - run_terraform_tests || EXIT_CODE=$? - echo "" - run_bicep_tests || EXIT_CODE=$? - ;; + terraform) + run_terraform_tests || EXIT_CODE=$? + ;; + bicep) + run_bicep_tests || EXIT_CODE=$? + ;; + both) + run_terraform_tests || EXIT_CODE=$? + echo "" + run_bicep_tests || EXIT_CODE=$? + ;; esac echo "" if [[ $EXIT_CODE -eq 0 ]]; then - echo -e "${GREEN}=== All tests completed successfully ===${NC}" + echo -e "${GREEN}=== All tests completed successfully ===${NC}" else - echo -e "${RED}=== Some tests failed ===${NC}" + echo -e "${RED}=== Some tests failed ===${NC}" fi exit $EXIT_CODE diff --git a/docs/_server/schemas/index.js b/docs/_server/schemas/index.js index b6f7fc50..46d605f9 100644 --- a/docs/_server/schemas/index.js +++ b/docs/_server/schemas/index.js @@ -130,10 +130,10 @@ async function loadSchema(schemaType) { schemaCache.set(schemaType, validate); return validate; } catch (retryError) { - throw new Error(`Failed to load schema ${schemaType} after retry: ${retryError.message}`); + throw new Error(`Failed to load schema ${schemaType} after retry: ${retryError.message}`, { cause: retryError }); } } - throw new Error(`Failed to load schema ${schemaType}: ${error.message}`); + throw new Error(`Failed to load schema ${schemaType}: ${error.message}`, { cause: error }); } } diff --git a/docs/_server/tests/integration/frontend-backend-integration.test.js b/docs/_server/tests/integration/frontend-backend-integration.test.js index 0c4abe75..049f4423 100644 --- a/docs/_server/tests/integration/frontend-backend-integration.test.js +++ b/docs/_server/tests/integration/frontend-backend-integration.test.js @@ -100,8 +100,8 @@ describe('Frontend-Backend Integration Tests', () => { server.close((err) => { clearTimeout(timeout); - if (err) reject(err); - else resolve(); + if (err) {reject(err);} + else {resolve();} }); }).catch(error => { console.warn('Error closing server:', error.message); diff --git a/docs/_server/tests/integration/security-attacks.test.js b/docs/_server/tests/integration/security-attacks.test.js index f03ac2e9..0843c4ea 100644 --- a/docs/_server/tests/integration/security-attacks.test.js +++ b/docs/_server/tests/integration/security-attacks.test.js @@ -127,11 +127,11 @@ describe('Security Attack Protection', () => { const nosqlInjectionPayload = { type: 'kata-progress', metadata: { - kataId: { $ne: null }, // Objects not allowed in metadata - title: { $regex: '.*' } // Objects not allowed in metadata + kataId: { $ne: null }, // Objects not allowed in metadata + title: { $regex: '.*' } // Objects not allowed in metadata }, progress: { - currentStep: { $gt: 0 }, // Objects not allowed + currentStep: { $gt: 0 }, // Objects not allowed completedSteps: [0] }, timestamp: new Date().toISOString() diff --git a/docs/_server/tests/routes/health.test.js b/docs/_server/tests/routes/health.test.js index 8282a0b6..15b615fe 100644 --- a/docs/_server/tests/routes/health.test.js +++ b/docs/_server/tests/routes/health.test.js @@ -8,7 +8,6 @@ import request from 'supertest'; describe('Health Check Routes', () => { let app; - let server; beforeEach(async () => { // Clear module cache to ensure fresh imports @@ -29,17 +28,13 @@ describe('Health Check Routes', () => { } } catch (error) { console.error('Error loading app:', error); - throw new Error('App not ready for testing'); + throw new Error('App not ready for testing', { cause: error }); } vi.clearAllMocks(); }, 10000); // Increase timeout to 10 seconds afterEach(async () => { - if (server) { - await new Promise(resolve => server.close(resolve)); - } - // Clear any cached modules if (global.gc) { global.gc(); diff --git a/docs/_server/tests/routes/learning-paths.test.js b/docs/_server/tests/routes/learning-paths.test.js index 244f4085..64d36555 100644 --- a/docs/_server/tests/routes/learning-paths.test.js +++ b/docs/_server/tests/routes/learning-paths.test.js @@ -625,9 +625,9 @@ describe('Learning Path File Creation', () => { .post('/api/learning/selections') .send({ selectedItems: [ - 'ai-assisted-engineering-01', // kata - 'foundation-ai-first-engineering', // path - 'lab-ai-deployment-01' // lab + 'ai-assisted-engineering-01', // kata + 'foundation-ai-first-engineering', // path + 'lab-ai-deployment-01' // lab ], userId: 'test-user' }) diff --git a/docs/_server/utils/learning-path-parser.js b/docs/_server/utils/learning-path-parser.js index 5747a130..df9d6754 100644 --- a/docs/_server/utils/learning-path-parser.js +++ b/docs/_server/utils/learning-path-parser.js @@ -40,7 +40,7 @@ class LearningPathParser { } parsePathMarkdown(markdownContent, fileName) { - const kataLinkRegex = /\[.*?\]\(\.\.\/katas\/([^/]+)\/([^)\.]+)\.md\)/g; + const kataLinkRegex = /\[.*?\]\(\.\.\/katas\/([^/]+)\/([^).]+)\.md\)/g; const kataIds = []; const seenIds = new Set(); diff --git a/docs/_server/utils/progress-operations.js b/docs/_server/utils/progress-operations.js index c5806b2d..f0574943 100644 --- a/docs/_server/utils/progress-operations.js +++ b/docs/_server/utils/progress-operations.js @@ -187,7 +187,7 @@ export async function getLatestProgressFile() { } catch (error) { // Directory doesn't exist or can't be read if (error.code === 'ENOENT') { - throw new Error('No progress files found'); + throw new Error('No progress files found', { cause: error }); } else { throw error; } diff --git a/docs/assets/js/features/assessment-completion-modal.js b/docs/assets/js/features/assessment-completion-modal.js index 870eeca3..ad1f94bc 100644 --- a/docs/assets/js/features/assessment-completion-modal.js +++ b/docs/assets/js/features/assessment-completion-modal.js @@ -139,7 +139,7 @@ class AssessmentCompletionModal { const skillBadge = modal.querySelector('.skill-badge'); // Get skill level from recommendations (use recommended path type directly) - let skillLevel = recommendations?.recommendedPathType || + const skillLevel = recommendations?.recommendedPathType || recommendations?.recommendedPath || assessmentResults?.overallLevel || assessmentResults?.results?.overallLevel || @@ -180,7 +180,7 @@ class AssessmentCompletionModal { } }; - const pathInfo = pathTypeMap[skillLevel] || pathTypeMap['beginner']; + const pathInfo = pathTypeMap[skillLevel] || pathTypeMap.beginner; const pathTitle = modal.querySelector('.path-title'); pathTitle.textContent = pathInfo.title; @@ -242,7 +242,7 @@ class AssessmentCompletionModal { 'expert': 'Expert' }; return levelMap[skillLevel] || 'Beginner'; - } /** + } /** * Sets up modal event listeners */ async setupModalEventListeners() { @@ -268,7 +268,7 @@ class AssessmentCompletionModal { // Overlay and keyboard handlers modal.addEventListener('click', (e) => { - if (e.target === modal) this.closeModal('closed'); + if (e.target === modal) {this.closeModal('closed');} }); const escapeHandler = (e) => { @@ -281,7 +281,7 @@ class AssessmentCompletionModal { // Store handler for cleanup this.escapeHandler = escapeHandler; - } /** + } /** * Saves the recommended learning path to user's dashboard via API * Dashboard will load selections from server on page load */ @@ -334,7 +334,7 @@ class AssessmentCompletionModal { 'intermediate': ['path-intermediate-infrastructure-architect'], 'expert': ['path-expert-enterprise-integration'] }; - newPaths = pathDefaults[targetDifficulty] || pathDefaults['foundation']; + newPaths = pathDefaults[targetDifficulty] || pathDefaults.foundation; } // Merge new paths with existing selections (deduplicate with Set) @@ -475,11 +475,11 @@ class AssessmentCompletionModal { this.escapeHandler = null; } } - } /** + } /** * Adds CSS styles for the modal */ addModalStyles() { - if (document.getElementById('assessment-modal-styles')) return; + if (document.getElementById('assessment-modal-styles')) {return;} const styles = `