ai-first-cli 1.3.1 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ai-dev/index.db +0 -0
- package/BETA_EVALUATION_REPORT.md +151 -0
- package/CHANGELOG.md +178 -0
- package/PHASE1_USER_SIMULATION.md +56 -0
- package/PHASE2_USER_SIMULATION.md +81 -0
- package/PHASE3_USER_SIMULATION.md +176 -0
- package/README.es.md +18 -0
- package/README.md +80 -1
- package/ai/graph/knowledge-graph.json +10 -0
- package/ai-context/ai_context.md +130 -0
- package/{test-projects/react-app/.ai-dev → ai-context}/ai_rules.md +10 -5
- package/ai-context/architecture.md +136 -0
- package/ai-context/context/features/src.json +69 -0
- package/ai-context/context/features/test-projects.json +69 -0
- package/ai-context/context/flows/App.json +17 -0
- package/ai-context/context/flows/DashboardPage.json +14 -0
- package/ai-context/context/flows/LoginPage.json +14 -0
- package/ai-context/context/flows/admin.json +10 -0
- package/ai-context/context/flows/ai-first.json +9 -0
- package/ai-context/context/flows/androidresources.json +11 -0
- package/ai-context/context/flows/auth.json +13 -0
- package/ai-context/context/flows/authController.json +14 -0
- package/ai-context/context/flows/doctor.json +9 -0
- package/ai-context/context/flows/entrypoints.json +9 -0
- package/ai-context/context/flows/explore.json +9 -0
- package/ai-context/context/flows/fastapiAdapter.json +14 -0
- package/ai-context/context/flows/fastapiadapter.json +11 -0
- package/ai-context/context/flows/index.json +19 -0
- package/ai-context/context/flows/indexer.json +9 -0
- package/ai-context/context/flows/indexstate.json +9 -0
- package/ai-context/context/flows/init.json +22 -0
- package/ai-context/context/flows/main.json +18 -0
- package/ai-context/context/flows/mainactivity.json +9 -0
- package/ai-context/context/flows/models.json +15 -0
- package/ai-context/context/flows/posts.json +15 -0
- package/ai-context/context/flows/repoMapper.json +20 -0
- package/ai-context/context/flows/repomapper.json +11 -0
- package/ai-context/context/flows/routes.json +15 -0
- package/ai-context/context/flows/serializers.json +10 -0
- package/ai-context/context/flows/user.json +23 -0
- package/ai-context/context/flows/views.json +12 -0
- package/{test-projects/react-app/.ai-dev → ai-context}/conventions.md +3 -2
- package/ai-context/dependencies.json +3360 -0
- package/ai-context/entrypoints.md +45 -0
- package/ai-context/index-state.json +196 -0
- package/ai-context/modules.json +901 -0
- package/ai-context/project.json +33 -0
- package/ai-context/repo_map.json +8857 -0
- package/ai-context/repo_map.md +2002 -0
- package/{test-projects/flask-app/.ai-dev → ai-context}/schema.json +1 -1
- package/ai-context/summary.md +46 -0
- package/ai-context/symbols.json +82467 -0
- package/{test-projects/react-app/.ai-dev → ai-context}/tech_stack.md +15 -7
- package/ai-context-evaluation-report-1774223059505.md +206 -0
- package/dist/analyzers/architecture.d.ts.map +1 -1
- package/dist/analyzers/architecture.js +6 -0
- package/dist/analyzers/architecture.js.map +1 -1
- package/dist/analyzers/entrypoints.d.ts.map +1 -1
- package/dist/analyzers/entrypoints.js +105 -0
- package/dist/analyzers/entrypoints.js.map +1 -1
- package/dist/analyzers/symbols.d.ts.map +1 -1
- package/dist/analyzers/symbols.js +72 -1
- package/dist/analyzers/symbols.js.map +1 -1
- package/dist/analyzers/techStack.d.ts +8 -0
- package/dist/analyzers/techStack.d.ts.map +1 -1
- package/dist/analyzers/techStack.js +75 -0
- package/dist/analyzers/techStack.js.map +1 -1
- package/dist/scripts/ai-context-evaluator.js +367 -0
- package/package.json +1 -1
- package/quick-evaluation-report-1774396002305.md +64 -0
- package/quick-evaluator.ts +200 -0
- package/scripts/ai-context-evaluator.ts +440 -0
- package/src/analyzers/architecture.ts +8 -0
- package/src/analyzers/entrypoints.ts +115 -0
- package/src/analyzers/symbols.ts +77 -1
- package/src/analyzers/techStack.ts +93 -0
- package/tests/apex-parser.test.ts +193 -0
- package/tests/cli-commands-batch1.test.ts +808 -0
- package/tests/cli-commands-batch2.test.ts +1113 -0
- package/tests/cli-commands-batch3.test.ts +1128 -0
- package/tests/cli-index.test.ts +1007 -0
- package/tests/cli-init.test.ts +761 -0
- package/tests/salesforce-apex-classes.test.ts +713 -0
- package/tests/salesforce-apex-triggers.test.ts +871 -0
- package/tests/salesforce-custom-objects.test.ts +918 -0
- package/tests/salesforce-flows.test.ts +710 -0
- package/tests/salesforce-lwc.test.ts +963 -0
- package/tests/salesforce-sfdx-integration.test.ts +1125 -0
- package/ANALISIS_COMPLETO.md +0 -424
- package/ANALISIS_MEJORAS.md +0 -327
- package/CONTRIBUTING.md +0 -89
- package/FLOW.md +0 -129
- package/TEST_RESULTS.md +0 -198
- package/TEST_RESULTS_COMPARATIVE.md +0 -159
- package/TEST_RESULTS_COMPLETE.md +0 -127
- package/TEST_RESULTS_COMPREHENSIVE.md +0 -208
- package/install.sh +0 -188
- package/run-all-tests.sh +0 -184
- package/test-ai-context-understanding.sh +0 -21
- package/test-projects/django-app/.ai-dev/ai_context.md +0 -92
- package/test-projects/django-app/.ai-dev/ai_rules.md +0 -47
- package/test-projects/django-app/.ai-dev/architecture.md +0 -57
- package/test-projects/django-app/.ai-dev/cache.json +0 -169
- package/test-projects/django-app/.ai-dev/context/flows/views.json +0 -10
- package/test-projects/django-app/.ai-dev/conventions.md +0 -51
- package/test-projects/django-app/.ai-dev/dependencies.json +0 -312
- package/test-projects/django-app/.ai-dev/entrypoints.md +0 -4
- package/test-projects/django-app/.ai-dev/files.json +0 -209
- package/test-projects/django-app/.ai-dev/graph/knowledge-graph.json +0 -36
- package/test-projects/django-app/.ai-dev/graph/module-graph.json +0 -145
- package/test-projects/django-app/.ai-dev/graph/symbol-graph.json +0 -1488
- package/test-projects/django-app/.ai-dev/graph/symbol-references.json +0 -1
- package/test-projects/django-app/.ai-dev/index-state.json +0 -294
- package/test-projects/django-app/.ai-dev/modules.json +0 -35
- package/test-projects/django-app/.ai-dev/project.json +0 -11
- package/test-projects/django-app/.ai-dev/repo_map.json +0 -412
- package/test-projects/django-app/.ai-dev/repo_map.md +0 -105
- package/test-projects/django-app/.ai-dev/schema.json +0 -5
- package/test-projects/django-app/.ai-dev/summary.md +0 -15
- package/test-projects/django-app/.ai-dev/symbols.json +0 -1
- package/test-projects/django-app/.ai-dev/tech_stack.md +0 -32
- package/test-projects/django-app/README.md +0 -91
- package/test-projects/django-app/blog/__init__.py +0 -0
- package/test-projects/django-app/blog/admin.py +0 -31
- package/test-projects/django-app/blog/models.py +0 -55
- package/test-projects/django-app/blog/serializers.py +0 -69
- package/test-projects/django-app/blog/urls.py +0 -14
- package/test-projects/django-app/blog/views.py +0 -96
- package/test-projects/django-app/django_app/__init__.py +0 -0
- package/test-projects/django-app/django_app/settings.py +0 -90
- package/test-projects/django-app/django_app/urls.py +0 -11
- package/test-projects/django-app/django_app/wsgi.py +0 -9
- package/test-projects/django-app/manage.py +0 -23
- package/test-projects/django-app/requirements.txt +0 -3
- package/test-projects/django-app/users/__init__.py +0 -0
- package/test-projects/django-app/users/admin.py +0 -42
- package/test-projects/django-app/users/models.py +0 -54
- package/test-projects/django-app/users/serializers.py +0 -113
- package/test-projects/django-app/users/urls.py +0 -13
- package/test-projects/django-app/users/views.py +0 -135
- package/test-projects/express-api/.ai-dev/ai_context.md +0 -112
- package/test-projects/express-api/.ai-dev/ai_rules.md +0 -50
- package/test-projects/express-api/.ai-dev/architecture.md +0 -62
- package/test-projects/express-api/.ai-dev/context/features/controllers.json +0 -13
- package/test-projects/express-api/.ai-dev/context/features/services.json +0 -13
- package/test-projects/express-api/.ai-dev/context/flows/auth.json +0 -12
- package/test-projects/express-api/.ai-dev/context/flows/user.json +0 -13
- package/test-projects/express-api/.ai-dev/conventions.md +0 -51
- package/test-projects/express-api/.ai-dev/dependencies.json +0 -54
- package/test-projects/express-api/.ai-dev/entrypoints.md +0 -17
- package/test-projects/express-api/.ai-dev/modules.json +0 -30
- package/test-projects/express-api/.ai-dev/project.json +0 -15
- package/test-projects/express-api/.ai-dev/repo_map.json +0 -100
- package/test-projects/express-api/.ai-dev/repo_map.md +0 -36
- package/test-projects/express-api/.ai-dev/schema.json +0 -5
- package/test-projects/express-api/.ai-dev/summary.md +0 -14
- package/test-projects/express-api/.ai-dev/symbols.json +0 -7
- package/test-projects/express-api/.ai-dev/tech_stack.md +0 -38
- package/test-projects/express-api/.ai-dev/tools.json +0 -10
- package/test-projects/express-api/controllers/authController.js +0 -32
- package/test-projects/express-api/controllers/userController.js +0 -51
- package/test-projects/express-api/index.js +0 -30
- package/test-projects/express-api/middleware/authMiddleware.js +0 -30
- package/test-projects/express-api/models/userRepository.js +0 -25
- package/test-projects/express-api/package.json +0 -18
- package/test-projects/express-api/services/authService.js +0 -17
- package/test-projects/express-api/services/userService.js +0 -28
- package/test-projects/fastapi-app/.ai-dev/ai_context.md +0 -89
- package/test-projects/fastapi-app/.ai-dev/ai_rules.md +0 -47
- package/test-projects/fastapi-app/.ai-dev/architecture.md +0 -39
- package/test-projects/fastapi-app/.ai-dev/cache.json +0 -125
- package/test-projects/fastapi-app/.ai-dev/conventions.md +0 -51
- package/test-projects/fastapi-app/.ai-dev/dependencies.json +0 -244
- package/test-projects/fastapi-app/.ai-dev/entrypoints.md +0 -4
- package/test-projects/fastapi-app/.ai-dev/files.json +0 -154
- package/test-projects/fastapi-app/.ai-dev/graph/knowledge-graph.json +0 -15
- package/test-projects/fastapi-app/.ai-dev/graph/module-graph.json +0 -78
- package/test-projects/fastapi-app/.ai-dev/graph/symbol-graph.json +0 -1724
- package/test-projects/fastapi-app/.ai-dev/graph/symbol-references.json +0 -51
- package/test-projects/fastapi-app/.ai-dev/index-state.json +0 -217
- package/test-projects/fastapi-app/.ai-dev/modules.json +0 -16
- package/test-projects/fastapi-app/.ai-dev/project.json +0 -9
- package/test-projects/fastapi-app/.ai-dev/repo_map.json +0 -298
- package/test-projects/fastapi-app/.ai-dev/repo_map.md +0 -74
- package/test-projects/fastapi-app/.ai-dev/schema.json +0 -5
- package/test-projects/fastapi-app/.ai-dev/summary.md +0 -12
- package/test-projects/fastapi-app/.ai-dev/symbols.json +0 -1
- package/test-projects/fastapi-app/.ai-dev/tech_stack.md +0 -32
- package/test-projects/fastapi-app/.ai-dev/tools.json +0 -10
- package/test-projects/fastapi-app/README.md +0 -118
- package/test-projects/fastapi-app/app/database.py +0 -21
- package/test-projects/fastapi-app/app/dependencies.py +0 -107
- package/test-projects/fastapi-app/app/main.py +0 -47
- package/test-projects/fastapi-app/app/models.py +0 -149
- package/test-projects/fastapi-app/app/routers/auth.py +0 -117
- package/test-projects/fastapi-app/app/routers/posts.py +0 -272
- package/test-projects/fastapi-app/app/schemas.py +0 -191
- package/test-projects/fastapi-app/requirements.txt +0 -10
- package/test-projects/flask-app/.ai-dev/ai_context.md +0 -94
- package/test-projects/flask-app/.ai-dev/ai_rules.md +0 -47
- package/test-projects/flask-app/.ai-dev/architecture.md +0 -49
- package/test-projects/flask-app/.ai-dev/cache.json +0 -157
- package/test-projects/flask-app/.ai-dev/context/features/app.json +0 -25
- package/test-projects/flask-app/.ai-dev/context/flows/routes.json +0 -14
- package/test-projects/flask-app/.ai-dev/conventions.md +0 -51
- package/test-projects/flask-app/.ai-dev/dependencies.json +0 -298
- package/test-projects/flask-app/.ai-dev/entrypoints.md +0 -4
- package/test-projects/flask-app/.ai-dev/files.json +0 -194
- package/test-projects/flask-app/.ai-dev/graph/knowledge-graph.json +0 -60
- package/test-projects/flask-app/.ai-dev/graph/module-graph.json +0 -95
- package/test-projects/flask-app/.ai-dev/graph/symbol-graph.json +0 -1448
- package/test-projects/flask-app/.ai-dev/graph/symbol-references.json +0 -45
- package/test-projects/flask-app/.ai-dev/index-state.json +0 -273
- package/test-projects/flask-app/.ai-dev/modules.json +0 -21
- package/test-projects/flask-app/.ai-dev/project.json +0 -13
- package/test-projects/flask-app/.ai-dev/repo_map.json +0 -400
- package/test-projects/flask-app/.ai-dev/repo_map.md +0 -98
- package/test-projects/flask-app/.ai-dev/summary.md +0 -13
- package/test-projects/flask-app/.ai-dev/symbols.json +0 -1
- package/test-projects/flask-app/.ai-dev/tech_stack.md +0 -32
- package/test-projects/flask-app/.ai-dev/tools.json +0 -10
- package/test-projects/flask-app/README.md +0 -129
- package/test-projects/flask-app/app/__init__.py +0 -46
- package/test-projects/flask-app/app/api/__init__.py +0 -7
- package/test-projects/flask-app/app/api/routes.py +0 -122
- package/test-projects/flask-app/app/auth/__init__.py +0 -7
- package/test-projects/flask-app/app/auth/forms.py +0 -52
- package/test-projects/flask-app/app/auth/routes.py +0 -68
- package/test-projects/flask-app/app/blog/__init__.py +0 -7
- package/test-projects/flask-app/app/blog/forms.py +0 -35
- package/test-projects/flask-app/app/blog/routes.py +0 -140
- package/test-projects/flask-app/app/main/__init__.py +0 -7
- package/test-projects/flask-app/app/main/routes.py +0 -88
- package/test-projects/flask-app/app/models.py +0 -177
- package/test-projects/flask-app/config.py +0 -64
- package/test-projects/flask-app/requirements.txt +0 -10
- package/test-projects/laravel-app/.ai-dev/ai_context.md +0 -97
- package/test-projects/laravel-app/.ai-dev/ai_rules.md +0 -47
- package/test-projects/laravel-app/.ai-dev/architecture.md +0 -60
- package/test-projects/laravel-app/.ai-dev/cache.json +0 -161
- package/test-projects/laravel-app/.ai-dev/context/features/app.json +0 -21
- package/test-projects/laravel-app/.ai-dev/context/flows/.json +0 -9
- package/test-projects/laravel-app/.ai-dev/context/flows/category.json +0 -12
- package/test-projects/laravel-app/.ai-dev/context/flows/comment.json +0 -12
- package/test-projects/laravel-app/.ai-dev/context/flows/post.json +0 -12
- package/test-projects/laravel-app/.ai-dev/context/flows/unnamed.json +0 -9
- package/test-projects/laravel-app/.ai-dev/conventions.md +0 -51
- package/test-projects/laravel-app/.ai-dev/dependencies.json +0 -6
- package/test-projects/laravel-app/.ai-dev/entrypoints.md +0 -4
- package/test-projects/laravel-app/.ai-dev/files.json +0 -199
- package/test-projects/laravel-app/.ai-dev/graph/knowledge-graph.json +0 -98
- package/test-projects/laravel-app/.ai-dev/graph/module-graph.json +0 -30
- package/test-projects/laravel-app/.ai-dev/graph/symbol-graph.json +0 -5
- package/test-projects/laravel-app/.ai-dev/graph/symbol-references.json +0 -1
- package/test-projects/laravel-app/.ai-dev/index-state.json +0 -280
- package/test-projects/laravel-app/.ai-dev/modules.json +0 -29
- package/test-projects/laravel-app/.ai-dev/project.json +0 -17
- package/test-projects/laravel-app/.ai-dev/repo_map.json +0 -419
- package/test-projects/laravel-app/.ai-dev/repo_map.md +0 -106
- package/test-projects/laravel-app/.ai-dev/schema.json +0 -5
- package/test-projects/laravel-app/.ai-dev/summary.md +0 -15
- package/test-projects/laravel-app/.ai-dev/symbols.json +0 -1
- package/test-projects/laravel-app/.ai-dev/tech_stack.md +0 -34
- package/test-projects/laravel-app/.ai-dev/tools.json +0 -10
- package/test-projects/laravel-app/README.md +0 -107
- package/test-projects/laravel-app/app/Http/Controllers/Api/CategoryController.php +0 -88
- package/test-projects/laravel-app/app/Http/Controllers/Api/CommentController.php +0 -56
- package/test-projects/laravel-app/app/Http/Controllers/Api/PostController.php +0 -174
- package/test-projects/laravel-app/app/Http/Controllers/Controller.php +0 -12
- package/test-projects/laravel-app/app/Models/Category.php +0 -34
- package/test-projects/laravel-app/app/Models/Comment.php +0 -51
- package/test-projects/laravel-app/app/Models/Post.php +0 -108
- package/test-projects/laravel-app/app/Models/User.php +0 -85
- package/test-projects/laravel-app/bootstrap/app.php +0 -25
- package/test-projects/laravel-app/composer.json +0 -35
- package/test-projects/laravel-app/routes/api.php +0 -40
- package/test-projects/nestjs-backend/.ai-dev/ai_context.md +0 -111
- package/test-projects/nestjs-backend/.ai-dev/ai_rules.md +0 -52
- package/test-projects/nestjs-backend/.ai-dev/architecture.md +0 -49
- package/test-projects/nestjs-backend/.ai-dev/cache.json +0 -169
- package/test-projects/nestjs-backend/.ai-dev/context/features/src.json +0 -23
- package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.controller.json +0 -14
- package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.json +0 -10
- package/test-projects/nestjs-backend/.ai-dev/context/flows/users..json +0 -10
- package/test-projects/nestjs-backend/.ai-dev/context/flows/users.controller.json +0 -14
- package/test-projects/nestjs-backend/.ai-dev/context/flows/users.json +0 -10
- package/test-projects/nestjs-backend/.ai-dev/conventions.md +0 -52
- package/test-projects/nestjs-backend/.ai-dev/dependencies.json +0 -152
- package/test-projects/nestjs-backend/.ai-dev/entrypoints.md +0 -18
- package/test-projects/nestjs-backend/.ai-dev/files.json +0 -209
- package/test-projects/nestjs-backend/.ai-dev/graph/knowledge-graph.json +0 -132
- package/test-projects/nestjs-backend/.ai-dev/graph/module-graph.json +0 -29
- package/test-projects/nestjs-backend/.ai-dev/graph/symbol-graph.json +0 -304
- package/test-projects/nestjs-backend/.ai-dev/graph/symbol-references.json +0 -5
- package/test-projects/nestjs-backend/.ai-dev/index-state.json +0 -294
- package/test-projects/nestjs-backend/.ai-dev/modules.json +0 -19
- package/test-projects/nestjs-backend/.ai-dev/project.json +0 -18
- package/test-projects/nestjs-backend/.ai-dev/repo_map.json +0 -427
- package/test-projects/nestjs-backend/.ai-dev/repo_map.md +0 -104
- package/test-projects/nestjs-backend/.ai-dev/schema.json +0 -5
- package/test-projects/nestjs-backend/.ai-dev/summary.md +0 -13
- package/test-projects/nestjs-backend/.ai-dev/symbols.json +0 -1
- package/test-projects/nestjs-backend/.ai-dev/tech_stack.md +0 -38
- package/test-projects/nestjs-backend/.ai-dev/tools.json +0 -10
- package/test-projects/nestjs-backend/package.json +0 -22
- package/test-projects/nestjs-backend/src/app.module.ts +0 -8
- package/test-projects/nestjs-backend/src/auth/auth.controller.ts +0 -22
- package/test-projects/nestjs-backend/src/auth/auth.module.ts +0 -11
- package/test-projects/nestjs-backend/src/auth/auth.service.ts +0 -28
- package/test-projects/nestjs-backend/src/auth/dto/login.dto.ts +0 -4
- package/test-projects/nestjs-backend/src/auth/strategies/jwt.strategy.ts +0 -18
- package/test-projects/nestjs-backend/src/main.ts +0 -9
- package/test-projects/nestjs-backend/src/users/users.controller.ts +0 -32
- package/test-projects/nestjs-backend/src/users/users.module.ts +0 -10
- package/test-projects/nestjs-backend/src/users/users.service.ts +0 -42
- package/test-projects/nestjs-backend/tsconfig.json +0 -21
- package/test-projects/python-cli/.ai-dev/ai_context.md +0 -95
- package/test-projects/python-cli/.ai-dev/ai_rules.md +0 -47
- package/test-projects/python-cli/.ai-dev/architecture.md +0 -55
- package/test-projects/python-cli/.ai-dev/cache.json +0 -149
- package/test-projects/python-cli/.ai-dev/context/features/cli.json +0 -16
- package/test-projects/python-cli/.ai-dev/context/flows/list_.json +0 -9
- package/test-projects/python-cli/.ai-dev/context/flows/remove_.json +0 -9
- package/test-projects/python-cli/.ai-dev/conventions.md +0 -51
- package/test-projects/python-cli/.ai-dev/dependencies.json +0 -66
- package/test-projects/python-cli/.ai-dev/entrypoints.md +0 -4
- package/test-projects/python-cli/.ai-dev/files.json +0 -184
- package/test-projects/python-cli/.ai-dev/graph/knowledge-graph.json +0 -83
- package/test-projects/python-cli/.ai-dev/graph/module-graph.json +0 -31
- package/test-projects/python-cli/.ai-dev/graph/symbol-graph.json +0 -358
- package/test-projects/python-cli/.ai-dev/graph/symbol-references.json +0 -11
- package/test-projects/python-cli/.ai-dev/index-state.json +0 -259
- package/test-projects/python-cli/.ai-dev/modules.json +0 -21
- package/test-projects/python-cli/.ai-dev/project.json +0 -15
- package/test-projects/python-cli/.ai-dev/repo_map.json +0 -367
- package/test-projects/python-cli/.ai-dev/repo_map.md +0 -93
- package/test-projects/python-cli/.ai-dev/schema.json +0 -5
- package/test-projects/python-cli/.ai-dev/summary.md +0 -14
- package/test-projects/python-cli/.ai-dev/symbols.json +0 -1
- package/test-projects/python-cli/.ai-dev/tech_stack.md +0 -32
- package/test-projects/python-cli/.ai-dev/tools.json +0 -10
- package/test-projects/python-cli/__init__.py +0 -1
- package/test-projects/python-cli/cli/__init__.py +0 -1
- package/test-projects/python-cli/cli/add_command.py +0 -6
- package/test-projects/python-cli/cli/list_command.py +0 -7
- package/test-projects/python-cli/cli/remove_command.py +0 -6
- package/test-projects/python-cli/main.py +0 -34
- package/test-projects/python-cli/models/__init__.py +0 -2
- package/test-projects/python-cli/models/task.py +0 -19
- package/test-projects/python-cli/models/task_repository.py +0 -44
- package/test-projects/rails-app/.ai-dev/ai_context.md +0 -94
- package/test-projects/rails-app/.ai-dev/ai_rules.md +0 -47
- package/test-projects/rails-app/.ai-dev/architecture.md +0 -49
- package/test-projects/rails-app/.ai-dev/cache.json +0 -193
- package/test-projects/rails-app/.ai-dev/context/features/app.json +0 -24
- package/test-projects/rails-app/.ai-dev/context/features/config.json +0 -13
- package/test-projects/rails-app/.ai-dev/context/flows/application.json +0 -9
- package/test-projects/rails-app/.ai-dev/context/flows/application_.json +0 -9
- package/test-projects/rails-app/.ai-dev/context/flows/comments.json +0 -11
- package/test-projects/rails-app/.ai-dev/context/flows/comments_.json +0 -11
- package/test-projects/rails-app/.ai-dev/context/flows/posts.json +0 -11
- package/test-projects/rails-app/.ai-dev/context/flows/posts_.json +0 -11
- package/test-projects/rails-app/.ai-dev/context/flows/routes.json +0 -9
- package/test-projects/rails-app/.ai-dev/context/flows/users.json +0 -11
- package/test-projects/rails-app/.ai-dev/context/flows/users_.json +0 -11
- package/test-projects/rails-app/.ai-dev/conventions.md +0 -51
- package/test-projects/rails-app/.ai-dev/dependencies.json +0 -6
- package/test-projects/rails-app/.ai-dev/entrypoints.md +0 -4
- package/test-projects/rails-app/.ai-dev/files.json +0 -239
- package/test-projects/rails-app/.ai-dev/graph/knowledge-graph.json +0 -130
- package/test-projects/rails-app/.ai-dev/graph/module-graph.json +0 -27
- package/test-projects/rails-app/.ai-dev/graph/symbol-graph.json +0 -5
- package/test-projects/rails-app/.ai-dev/graph/symbol-references.json +0 -1
- package/test-projects/rails-app/.ai-dev/index-state.json +0 -336
- package/test-projects/rails-app/.ai-dev/modules.json +0 -26
- package/test-projects/rails-app/.ai-dev/project.json +0 -22
- package/test-projects/rails-app/.ai-dev/repo_map.json +0 -486
- package/test-projects/rails-app/.ai-dev/repo_map.md +0 -117
- package/test-projects/rails-app/.ai-dev/schema.json +0 -5
- package/test-projects/rails-app/.ai-dev/summary.md +0 -13
- package/test-projects/rails-app/.ai-dev/symbols.json +0 -1
- package/test-projects/rails-app/.ai-dev/tech_stack.md +0 -32
- package/test-projects/rails-app/.ai-dev/tools.json +0 -10
- package/test-projects/rails-app/Gemfile +0 -38
- package/test-projects/rails-app/README.md +0 -140
- package/test-projects/rails-app/Rakefile +0 -8
- package/test-projects/rails-app/app/controllers/api/comments_controller.rb +0 -75
- package/test-projects/rails-app/app/controllers/api/posts_controller.rb +0 -68
- package/test-projects/rails-app/app/controllers/api/users_controller.rb +0 -54
- package/test-projects/rails-app/app/controllers/application_controller.rb +0 -31
- package/test-projects/rails-app/app/models/comment.rb +0 -34
- package/test-projects/rails-app/app/models/post.rb +0 -36
- package/test-projects/rails-app/app/models/user.rb +0 -28
- package/test-projects/rails-app/app/services/post_service.rb +0 -92
- package/test-projects/rails-app/app/services/user_service.rb +0 -76
- package/test-projects/rails-app/config/application.rb +0 -27
- package/test-projects/rails-app/config/environment.rb +0 -7
- package/test-projects/rails-app/config/routes.rb +0 -15
- package/test-projects/react-app/.ai-dev/ai_context.md +0 -96
- package/test-projects/react-app/.ai-dev/architecture.md +0 -39
- package/test-projects/react-app/.ai-dev/cache.json +0 -153
- package/test-projects/react-app/.ai-dev/context/features/src.json +0 -18
- package/test-projects/react-app/.ai-dev/context/flows/UsersPage.json +0 -14
- package/test-projects/react-app/.ai-dev/context/flows/dashboard.json +0 -9
- package/test-projects/react-app/.ai-dev/context/flows/login.json +0 -9
- package/test-projects/react-app/.ai-dev/context/flows/users.json +0 -9
- package/test-projects/react-app/.ai-dev/dependencies.json +0 -128
- package/test-projects/react-app/.ai-dev/entrypoints.md +0 -4
- package/test-projects/react-app/.ai-dev/files.json +0 -189
- package/test-projects/react-app/.ai-dev/graph/knowledge-graph.json +0 -112
- package/test-projects/react-app/.ai-dev/graph/module-graph.json +0 -31
- package/test-projects/react-app/.ai-dev/graph/symbol-graph.json +0 -868
- package/test-projects/react-app/.ai-dev/graph/symbol-references.json +0 -31
- package/test-projects/react-app/.ai-dev/index-state.json +0 -266
- package/test-projects/react-app/.ai-dev/modules.json +0 -17
- package/test-projects/react-app/.ai-dev/project.json +0 -16
- package/test-projects/react-app/.ai-dev/repo_map.json +0 -391
- package/test-projects/react-app/.ai-dev/repo_map.md +0 -94
- package/test-projects/react-app/.ai-dev/schema.json +0 -5
- package/test-projects/react-app/.ai-dev/summary.md +0 -13
- package/test-projects/react-app/.ai-dev/symbols.json +0 -1
- package/test-projects/react-app/.ai-dev/tools.json +0 -10
- package/test-projects/react-app/package.json +0 -16
- package/test-projects/react-app/src/App.tsx +0 -21
- package/test-projects/react-app/src/context/AuthContext.tsx +0 -41
- package/test-projects/react-app/src/hooks/useAuth.ts +0 -10
- package/test-projects/react-app/src/main.tsx +0 -10
- package/test-projects/react-app/src/pages/DashboardPage.tsx +0 -17
- package/test-projects/react-app/src/pages/LoginPage.tsx +0 -41
- package/test-projects/react-app/src/pages/UsersPage.tsx +0 -36
- package/test-projects/react-app/src/services/userService.ts +0 -37
- package/test-projects/salesforce-cli/.ai-dev/ai_context.md +0 -89
- package/test-projects/salesforce-cli/.ai-dev/ai_rules.md +0 -47
- package/test-projects/salesforce-cli/.ai-dev/architecture.md +0 -39
- package/test-projects/salesforce-cli/.ai-dev/cache.json +0 -125
- package/test-projects/salesforce-cli/.ai-dev/context/features/force-app.json +0 -14
- package/test-projects/salesforce-cli/.ai-dev/context/flows/account.json +0 -9
- package/test-projects/salesforce-cli/.ai-dev/context/flows/opportunity.json +0 -9
- package/test-projects/salesforce-cli/.ai-dev/conventions.md +0 -51
- package/test-projects/salesforce-cli/.ai-dev/dependencies.json +0 -6
- package/test-projects/salesforce-cli/.ai-dev/entrypoints.md +0 -4
- package/test-projects/salesforce-cli/.ai-dev/files.json +0 -154
- package/test-projects/salesforce-cli/.ai-dev/graph/knowledge-graph.json +0 -64
- package/test-projects/salesforce-cli/.ai-dev/graph/module-graph.json +0 -13
- package/test-projects/salesforce-cli/.ai-dev/graph/symbol-graph.json +0 -148
- package/test-projects/salesforce-cli/.ai-dev/graph/symbol-references.json +0 -1
- package/test-projects/salesforce-cli/.ai-dev/index-state.json +0 -217
- package/test-projects/salesforce-cli/.ai-dev/modules.json +0 -12
- package/test-projects/salesforce-cli/.ai-dev/project.json +0 -14
- package/test-projects/salesforce-cli/.ai-dev/repo_map.json +0 -328
- package/test-projects/salesforce-cli/.ai-dev/repo_map.md +0 -80
- package/test-projects/salesforce-cli/.ai-dev/schema.json +0 -5
- package/test-projects/salesforce-cli/.ai-dev/summary.md +0 -13
- package/test-projects/salesforce-cli/.ai-dev/symbols.json +0 -1
- package/test-projects/salesforce-cli/.ai-dev/tech_stack.md +0 -31
- package/test-projects/salesforce-cli/.ai-dev/tools.json +0 -10
- package/test-projects/salesforce-cli/.forceignore +0 -27
- package/test-projects/salesforce-cli/force-app/main/default/classes/AccountController.cls +0 -24
- package/test-projects/salesforce-cli/force-app/main/default/classes/OpportunityController.cls +0 -25
- package/test-projects/salesforce-cli/force-app/main/default/objects/Project__c.object.xml +0 -45
- package/test-projects/salesforce-cli/force-app/main/default/triggers/AccountTrigger.trigger +0 -33
- package/test-projects/salesforce-cli/sfdx-project.json +0 -11
- package/test-projects/spring-boot-app/.ai-dev/ai_context.md +0 -91
- package/test-projects/spring-boot-app/.ai-dev/ai_rules.md +0 -48
- package/test-projects/spring-boot-app/.ai-dev/architecture.md +0 -39
- package/test-projects/spring-boot-app/.ai-dev/cache.json +0 -173
- package/test-projects/spring-boot-app/.ai-dev/context/features/src.json +0 -26
- package/test-projects/spring-boot-app/.ai-dev/context/flows/PostController.json +0 -19
- package/test-projects/spring-boot-app/.ai-dev/context/flows/UserController.json +0 -19
- package/test-projects/spring-boot-app/.ai-dev/context/flows/comment.json +0 -11
- package/test-projects/spring-boot-app/.ai-dev/context/flows/post.json +0 -14
- package/test-projects/spring-boot-app/.ai-dev/context/flows/user.json +0 -14
- package/test-projects/spring-boot-app/.ai-dev/conventions.md +0 -52
- package/test-projects/spring-boot-app/.ai-dev/dependencies.json +0 -326
- package/test-projects/spring-boot-app/.ai-dev/entrypoints.md +0 -4
- package/test-projects/spring-boot-app/.ai-dev/files.json +0 -214
- package/test-projects/spring-boot-app/.ai-dev/graph/knowledge-graph.json +0 -231
- package/test-projects/spring-boot-app/.ai-dev/graph/module-graph.json +0 -22
- package/test-projects/spring-boot-app/.ai-dev/graph/symbol-graph.json +0 -794
- package/test-projects/spring-boot-app/.ai-dev/graph/symbol-references.json +0 -70
- package/test-projects/spring-boot-app/.ai-dev/index-state.json +0 -301
- package/test-projects/spring-boot-app/.ai-dev/modules.json +0 -21
- package/test-projects/spring-boot-app/.ai-dev/project.json +0 -17
- package/test-projects/spring-boot-app/.ai-dev/repo_map.json +0 -461
- package/test-projects/spring-boot-app/.ai-dev/repo_map.md +0 -109
- package/test-projects/spring-boot-app/.ai-dev/schema.json +0 -5
- package/test-projects/spring-boot-app/.ai-dev/summary.md +0 -12
- package/test-projects/spring-boot-app/.ai-dev/symbols.json +0 -1
- package/test-projects/spring-boot-app/.ai-dev/tech_stack.md +0 -32
- package/test-projects/spring-boot-app/.ai-dev/tools.json +0 -10
- package/test-projects/spring-boot-app/.classpath +0 -57
- package/test-projects/spring-boot-app/.factorypath +0 -69
- package/test-projects/spring-boot-app/.project +0 -34
- package/test-projects/spring-boot-app/.settings/org.eclipse.core.resources.prefs +0 -4
- package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.apt.core.prefs +0 -4
- package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.core.prefs +0 -10
- package/test-projects/spring-boot-app/.settings/org.eclipse.m2e.core.prefs +0 -4
- package/test-projects/spring-boot-app/README.md +0 -122
- package/test-projects/spring-boot-app/pom.xml +0 -79
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/DemoApplication.java +0 -12
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/CommentController.java +0 -89
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/PostController.java +0 -92
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/UserController.java +0 -84
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Comment.java +0 -38
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Post.java +0 -56
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/User.java +0 -44
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/CommentRepository.java +0 -21
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/PostRepository.java +0 -18
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/UserRepository.java +0 -15
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/PostService.java +0 -83
- package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/UserService.java +0 -62
- package/test-projects/spring-boot-app/src/main/resources/application.properties +0 -22
- package/test-projects/spring-boot-app/target/classes/com/example/demo/DemoApplication.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentCreateRequest.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentUpdateRequest.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostCreateRequest.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostUpdateRequest.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserCreateRequest.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserUpdateRequest.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Comment.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Post.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/models/User.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/CommentRepository.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/PostRepository.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/UserRepository.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/services/PostService.class +0 -0
- package/test-projects/spring-boot-app/target/classes/com/example/demo/services/UserService.class +0 -0
- package/tests/e2e/run-e2e.sh +0 -88
- /package/{test-projects/django-app/.ai-dev → ai-context}/tools.json +0 -0
|
@@ -0,0 +1,1125 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll } from "vitest";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
const SALESFORCE_ENTERPRISE_PATH = path.join(process.cwd(), "test-projects/salesforce-enterprise");
|
|
6
|
+
|
|
7
|
+
describe("Salesforce SFDX Metadata and Integration Tests", () => {
|
|
8
|
+
|
|
9
|
+
// =========================================================================
|
|
10
|
+
// INTEGRATION TESTS - SFDX Project Detection
|
|
11
|
+
// =========================================================================
|
|
12
|
+
|
|
13
|
+
describe("Integration: SFDX Project Detection", () => {
|
|
14
|
+
|
|
15
|
+
it("should detect sfdx-project.json in project root", () => {
|
|
16
|
+
const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
|
|
17
|
+
expect(fs.existsSync(sfdxProjectPath)).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should detect package.xml in force-app/main/default", () => {
|
|
21
|
+
const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
|
|
22
|
+
expect(fs.existsSync(packageXmlPath)).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should detect Admin profile", () => {
|
|
26
|
+
const profilePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml");
|
|
27
|
+
expect(fs.existsSync(profilePath)).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should detect SalesRepresentative permission set", () => {
|
|
31
|
+
const permSetPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml");
|
|
32
|
+
expect(fs.existsSync(permSetPath)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should have complete SFDX project structure", () => {
|
|
36
|
+
// Verify key directories exist
|
|
37
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app"))).toBe(true);
|
|
38
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main"))).toBe(true);
|
|
39
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default"))).toBe(true);
|
|
40
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes"))).toBe(true);
|
|
41
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers"))).toBe(true);
|
|
42
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/lwc"))).toBe(true);
|
|
43
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows"))).toBe(true);
|
|
44
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/objects"))).toBe(true);
|
|
45
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles"))).toBe(true);
|
|
46
|
+
expect(fs.existsSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets"))).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// =========================================================================
|
|
51
|
+
// SFDX-PROJECT.JSON DETECTION AND PARSING
|
|
52
|
+
// =========================================================================
|
|
53
|
+
|
|
54
|
+
describe("sfdx-project.json Detection and Parsing", () => {
|
|
55
|
+
let sfdxProjectContent: string;
|
|
56
|
+
let sfdxProjectJson: Record<string, unknown>;
|
|
57
|
+
|
|
58
|
+
beforeAll(() => {
|
|
59
|
+
const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
|
|
60
|
+
sfdxProjectContent = fs.readFileSync(sfdxProjectPath, "utf-8");
|
|
61
|
+
sfdxProjectJson = JSON.parse(sfdxProjectContent);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("File Detection", () => {
|
|
65
|
+
it("should be valid JSON", () => {
|
|
66
|
+
expect(() => JSON.parse(sfdxProjectContent)).not.toThrow();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should have correct XML declaration", () => {
|
|
70
|
+
// sfdx-project.json doesn't have XML declaration, it's pure JSON
|
|
71
|
+
expect(sfdxProjectContent.trim().startsWith("{")).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("packageDirectories Verification", () => {
|
|
76
|
+
it("should have packageDirectories array", () => {
|
|
77
|
+
expect(sfdxProjectJson).toHaveProperty("packageDirectories");
|
|
78
|
+
expect(Array.isArray(sfdxProjectJson.packageDirectories)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should have force-app as package path", () => {
|
|
82
|
+
const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
|
|
83
|
+
expect(packageDirs[0]).toHaveProperty("path", "force-app");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should have default=true for force-app package", () => {
|
|
87
|
+
const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
|
|
88
|
+
expect(packageDirs[0]).toHaveProperty("default", true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should have package name SalesforceEnterprise", () => {
|
|
92
|
+
const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
|
|
93
|
+
expect(packageDirs[0]).toHaveProperty("package", "SalesforceEnterprise");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should have versionName", () => {
|
|
97
|
+
const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
|
|
98
|
+
expect(packageDirs[0]).toHaveProperty("versionName", "ver 1.0");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should have versionNumber", () => {
|
|
102
|
+
const packageDirs = sfdxProjectJson.packageDirectories as Array<Record<string, unknown>>;
|
|
103
|
+
expect(packageDirs[0]).toHaveProperty("versionNumber", "1.0.0.NEXT");
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("Project Properties Verification", () => {
|
|
108
|
+
it("should have name as SalesforceEnterprise", () => {
|
|
109
|
+
expect(sfdxProjectJson).toHaveProperty("name", "SalesforceEnterprise");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should have namespace as empty string", () => {
|
|
113
|
+
expect(sfdxProjectJson).toHaveProperty("namespace", "");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should have sfdcLoginUrl as https://login.salesforce.com", () => {
|
|
117
|
+
expect(sfdxProjectJson).toHaveProperty("sfdcLoginUrl", "https://login.salesforce.com");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should have singlePackage=false", () => {
|
|
121
|
+
expect(sfdxProjectJson).toHaveProperty("singlePackage", false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should have sourceApiVersion as 59.0", () => {
|
|
125
|
+
expect(sfdxProjectJson).toHaveProperty("sourceApiVersion", "59.0");
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("Complete JSON Structure", () => {
|
|
130
|
+
it("should have all required properties", () => {
|
|
131
|
+
expect(sfdxProjectJson).toHaveProperty("packageDirectories");
|
|
132
|
+
expect(sfdxProjectJson).toHaveProperty("name");
|
|
133
|
+
expect(sfdxProjectJson).toHaveProperty("namespace");
|
|
134
|
+
expect(sfdxProjectJson).toHaveProperty("sfdcLoginUrl");
|
|
135
|
+
expect(sfdxProjectJson).toHaveProperty("singlePackage");
|
|
136
|
+
expect(sfdxProjectJson).toHaveProperty("sourceApiVersion");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should match expected structure", () => {
|
|
140
|
+
expect(sfdxProjectContent).toContain('"packageDirectories"');
|
|
141
|
+
expect(sfdxProjectContent).toContain('"path": "force-app"');
|
|
142
|
+
expect(sfdxProjectContent).toContain('"default": true');
|
|
143
|
+
expect(sfdxProjectContent).toContain('"package": "SalesforceEnterprise"');
|
|
144
|
+
expect(sfdxProjectContent).toContain('"versionName": "ver 1.0"');
|
|
145
|
+
expect(sfdxProjectContent).toContain('"versionNumber": "1.0.0.NEXT"');
|
|
146
|
+
expect(sfdxProjectContent).toContain('"name": "SalesforceEnterprise"');
|
|
147
|
+
expect(sfdxProjectContent).toContain('"namespace": ""');
|
|
148
|
+
expect(sfdxProjectContent).toContain('"sfdcLoginUrl": "https://login.salesforce.com"');
|
|
149
|
+
expect(sfdxProjectContent).toContain('"singlePackage": false');
|
|
150
|
+
expect(sfdxProjectContent).toContain('"sourceApiVersion": "59.0"');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// =========================================================================
|
|
156
|
+
// PACKAGE.XML DETECTION AND COMPONENT LISTING
|
|
157
|
+
// =========================================================================
|
|
158
|
+
|
|
159
|
+
describe("package.xml Detection and Component Listing", () => {
|
|
160
|
+
let packageXmlContent: string;
|
|
161
|
+
|
|
162
|
+
beforeAll(() => {
|
|
163
|
+
const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
|
|
164
|
+
packageXmlContent = fs.readFileSync(packageXmlPath, "utf-8");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("File Detection", () => {
|
|
168
|
+
it("should have correct XML declaration", () => {
|
|
169
|
+
expect(packageXmlContent).toContain('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should have Package as root element", () => {
|
|
173
|
+
expect(packageXmlContent).toContain("<Package xmlns=");
|
|
174
|
+
expect(packageXmlContent).toContain("http://soap.sforce.com/2006/04/metadata");
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe("Component Types Detection", () => {
|
|
179
|
+
it("should have ApexClass type with 9 members", () => {
|
|
180
|
+
expect(packageXmlContent).toContain("<name>ApexClass</name>");
|
|
181
|
+
|
|
182
|
+
// Verify all 9 ApexClass members
|
|
183
|
+
const apexClasses = [
|
|
184
|
+
"AccountController",
|
|
185
|
+
"AccountControllerTest",
|
|
186
|
+
"OpportunityBatch",
|
|
187
|
+
"OpportunityBatchTest",
|
|
188
|
+
"OpportunityTriggerHandler",
|
|
189
|
+
"OpportunityTriggerHelper",
|
|
190
|
+
"Logger",
|
|
191
|
+
"TriggerManagement",
|
|
192
|
+
"SessionManagement"
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
for (const apexClass of apexClasses) {
|
|
196
|
+
expect(packageXmlContent).toContain(`<members>${apexClass}</members>`);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should have ApexTrigger type with 1 member", () => {
|
|
201
|
+
expect(packageXmlContent).toContain("<name>ApexTrigger</name>");
|
|
202
|
+
expect(packageXmlContent).toContain("<members>OpportunityTrigger</members>");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("should have LightningComponentBundle type with 2 members", () => {
|
|
206
|
+
expect(packageXmlContent).toContain("<name>LightningComponentBundle</name>");
|
|
207
|
+
expect(packageXmlContent).toContain("<members>accountList</members>");
|
|
208
|
+
expect(packageXmlContent).toContain("<members>opportunityCard</members>");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should have Flow type with 2 members", () => {
|
|
212
|
+
expect(packageXmlContent).toContain("<name>Flow</name>");
|
|
213
|
+
expect(packageXmlContent).toContain("<members>Account_Create</members>");
|
|
214
|
+
expect(packageXmlContent).toContain("<members>Opportunity_Update</members>");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("should have CustomObject type with 1 member", () => {
|
|
218
|
+
expect(packageXmlContent).toContain("<name>CustomObject</name>");
|
|
219
|
+
expect(packageXmlContent).toContain("<members>CustomObject__c</members>");
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("should have Profile type with 1 member", () => {
|
|
223
|
+
expect(packageXmlContent).toContain("<name>Profile</name>");
|
|
224
|
+
expect(packageXmlContent).toContain("<members>Admin</members>");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("should have PermissionSet type with 1 member", () => {
|
|
228
|
+
expect(packageXmlContent).toContain("<name>PermissionSet</name>");
|
|
229
|
+
expect(packageXmlContent).toContain("<members>SalesRepresentative</members>");
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe("Component Count Verification", () => {
|
|
234
|
+
it("should have 7 type definitions", () => {
|
|
235
|
+
const typeMatches = packageXmlContent.match(/<name>\w+<\/name>/g);
|
|
236
|
+
expect(typeMatches).toHaveLength(7);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should have correct component counts", () => {
|
|
240
|
+
// ApexClass: 9 members
|
|
241
|
+
expect(packageXmlContent.match(/<name>ApexClass<\/name>/g)).toHaveLength(1);
|
|
242
|
+
|
|
243
|
+
// ApexTrigger: 1 member
|
|
244
|
+
expect(packageXmlContent.match(/<name>ApexTrigger<\/name>/g)).toHaveLength(1);
|
|
245
|
+
|
|
246
|
+
// LightningComponentBundle: 2 members
|
|
247
|
+
expect(packageXmlContent.match(/<name>LightningComponentBundle<\/name>/g)).toHaveLength(1);
|
|
248
|
+
|
|
249
|
+
// Flow: 2 members
|
|
250
|
+
expect(packageXmlContent.match(/<name>Flow<\/name>/g)).toHaveLength(1);
|
|
251
|
+
|
|
252
|
+
// CustomObject: 1 member
|
|
253
|
+
expect(packageXmlContent.match(/<name>CustomObject<\/name>/g)).toHaveLength(1);
|
|
254
|
+
|
|
255
|
+
// Profile: 1 member
|
|
256
|
+
expect(packageXmlContent.match(/<name>Profile<\/name>/g)).toHaveLength(1);
|
|
257
|
+
|
|
258
|
+
// PermissionSet: 1 member
|
|
259
|
+
expect(packageXmlContent.match(/<name>PermissionSet<\/name>/g)).toHaveLength(1);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("should have version 59.0", () => {
|
|
263
|
+
expect(packageXmlContent).toContain("<version>59.0</version>");
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe("Package XML Structure", () => {
|
|
268
|
+
it("should contain all types sections", () => {
|
|
269
|
+
expect(packageXmlContent).toContain("<types>");
|
|
270
|
+
expect(packageXmlContent).toContain("</types>");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("should have types in correct order", () => {
|
|
274
|
+
const apexClassIdx = packageXmlContent.indexOf("<name>ApexClass</name>");
|
|
275
|
+
const apexTriggerIdx = packageXmlContent.indexOf("<name>ApexTrigger</name>");
|
|
276
|
+
const lwcIdx = packageXmlContent.indexOf("<name>LightningComponentBundle</name>");
|
|
277
|
+
const flowIdx = packageXmlContent.indexOf("<name>Flow</name>");
|
|
278
|
+
const customObjIdx = packageXmlContent.indexOf("<name>CustomObject</name>");
|
|
279
|
+
const profileIdx = packageXmlContent.indexOf("<name>Profile</name>");
|
|
280
|
+
const permSetIdx = packageXmlContent.indexOf("<name>PermissionSet</name>");
|
|
281
|
+
|
|
282
|
+
expect(apexClassIdx).toBeLessThan(apexTriggerIdx);
|
|
283
|
+
expect(apexTriggerIdx).toBeLessThan(lwcIdx);
|
|
284
|
+
expect(lwcIdx).toBeLessThan(flowIdx);
|
|
285
|
+
expect(flowIdx).toBeLessThan(customObjIdx);
|
|
286
|
+
expect(customObjIdx).toBeLessThan(profileIdx);
|
|
287
|
+
expect(profileIdx).toBeLessThan(permSetIdx);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// =========================================================================
|
|
293
|
+
// PROFILE METADATA (Admin.profile-meta.xml)
|
|
294
|
+
// =========================================================================
|
|
295
|
+
|
|
296
|
+
describe("Profile Metadata (Admin.profile-meta.xml)", () => {
|
|
297
|
+
let profileContent: string;
|
|
298
|
+
|
|
299
|
+
beforeAll(() => {
|
|
300
|
+
const profilePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml");
|
|
301
|
+
profileContent = fs.readFileSync(profilePath, "utf-8");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe("File Detection", () => {
|
|
305
|
+
it("should have correct XML declaration", () => {
|
|
306
|
+
expect(profileContent).toContain('<?xml version="1.0" encoding="UTF-8"?>');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("should have Profile as root element", () => {
|
|
310
|
+
expect(profileContent).toContain("<Profile xmlns=");
|
|
311
|
+
expect(profileContent).toContain("http://soap.sforce.com/2006/04/metadata");
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should have custom=false", () => {
|
|
315
|
+
expect(profileContent).toContain("<custom>false</custom>");
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe("User License", () => {
|
|
320
|
+
it("should have userLicense as Salesforce", () => {
|
|
321
|
+
expect(profileContent).toContain("<userLicense>Salesforce</userLicense>");
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe("Field Permissions", () => {
|
|
326
|
+
it("should have 12 fieldPermissions entries", () => {
|
|
327
|
+
const fieldPermissionMatches = profileContent.match(/<fieldPermissions>/g);
|
|
328
|
+
expect(fieldPermissionMatches).toHaveLength(12);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("should have field permissions for Account.Active__c", () => {
|
|
332
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
333
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<\/fieldPermissions>/);
|
|
334
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<editable>true<\/editable>[\s\S]*?<\/fieldPermissions>/);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("should have field permissions for Account.CustomerPriority__c", () => {
|
|
338
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.CustomerPriority__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should have field permissions for Account.SLA__c", () => {
|
|
342
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.SLA__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("should have field permissions for Account.Total_Opportunity_Amount__c (readable only)", () => {
|
|
346
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Total_Opportunity_Amount__c<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>false<\/editable>[\s\S]*?<\/fieldPermissions>/);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should have field permissions for Opportunity standard fields", () => {
|
|
350
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Amount<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
351
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.LeadSource<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
352
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Probability<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("should have field permissions for CustomObject__c fields", () => {
|
|
356
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Amount__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
357
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Category__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
358
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.CustomAccount__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
359
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.IsActive__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
360
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Stage__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
describe("Object Permissions", () => {
|
|
365
|
+
it("should have 3 objectPermissions entries", () => {
|
|
366
|
+
const objectPermissionMatches = profileContent.match(/<objectPermissions>/g);
|
|
367
|
+
expect(objectPermissionMatches).toHaveLength(3);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it("should have full access to Account", () => {
|
|
371
|
+
expect(profileContent).toMatch(/<objectPermissions>[\s\S]*?<object>Account<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>true<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>true<\/modifyAllRecords>[\s\S]*?<\/objectPermissions>/);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("should have full access to Opportunity", () => {
|
|
375
|
+
expect(profileContent).toMatch(/<objectPermissions>[\s\S]*?<object>Opportunity<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>true<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>true<\/modifyAllRecords>[\s\S]*?<\/objectPermissions>/);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("should have full access to CustomObject__c", () => {
|
|
379
|
+
const customObjPermMatch = profileContent.match(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
|
|
380
|
+
expect(customObjPermMatch).toBeTruthy();
|
|
381
|
+
const customObjSection = customObjPermMatch?.[0] || "";
|
|
382
|
+
expect(customObjSection).toContain("<allowCreate>true</allowCreate>");
|
|
383
|
+
expect(customObjSection).toContain("<allowDelete>true</allowDelete>");
|
|
384
|
+
expect(customObjSection).toContain("<allowEdit>true</allowEdit>");
|
|
385
|
+
expect(customObjSection).toContain("<allowRead>true</allowRead>");
|
|
386
|
+
expect(customObjSection).toContain("<modifyAllRecords>true</modifyAllRecords>");
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
describe("Class Accesses", () => {
|
|
391
|
+
it("should have 5 classAccesses entries", () => {
|
|
392
|
+
const classAccessMatches = profileContent.match(/<classAccesses>/g);
|
|
393
|
+
expect(classAccessMatches).toHaveLength(5);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("should enable AccountController", () => {
|
|
397
|
+
expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>AccountController<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("should enable OpportunityBatch", () => {
|
|
401
|
+
expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>OpportunityBatch<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("should enable OpportunityTriggerHandler", () => {
|
|
405
|
+
expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>OpportunityTriggerHandler<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("should enable Logger", () => {
|
|
409
|
+
expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>Logger<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it("should enable TriggerManagement", () => {
|
|
413
|
+
expect(profileContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>TriggerManagement<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
describe("Flow Accesses", () => {
|
|
418
|
+
it("should have 2 flowAccesses entries", () => {
|
|
419
|
+
const flowAccessMatches = profileContent.match(/<flowAccesses>/g);
|
|
420
|
+
expect(flowAccessMatches).toHaveLength(2);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it("should enable Account_Create flow", () => {
|
|
424
|
+
expect(profileContent).toMatch(/<flowAccesses>[\s\S]*?<flow>Account_Create<\/flow>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/flowAccesses>/);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it("should enable Opportunity_Update flow", () => {
|
|
428
|
+
expect(profileContent).toMatch(/<flowAccesses>[\s\S]*?<flow>Opportunity_Update<\/flow>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/flowAccesses>/);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
describe("Layout Assignments", () => {
|
|
433
|
+
it("should have 3 layoutAssignments", () => {
|
|
434
|
+
const layoutMatches = profileContent.match(/<layoutAssignments>/g);
|
|
435
|
+
expect(layoutMatches).toHaveLength(3);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("should have Account layout assignment", () => {
|
|
439
|
+
expect(profileContent).toContain("<layout>Account-Account Layout</layout>");
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it("should have Opportunity layout assignment", () => {
|
|
443
|
+
expect(profileContent).toContain("<layout>Opportunity-Opportunity Layout</layout>");
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it("should have CustomObject__c layout assignment", () => {
|
|
447
|
+
expect(profileContent).toContain("<layout>CustomObject__c-Custom Object Layout</layout>");
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
describe("Tab Visibilities", () => {
|
|
452
|
+
it("should have 3 tabVisibilities entries", () => {
|
|
453
|
+
const tabMatches = profileContent.match(/<tabVisibilities>/g);
|
|
454
|
+
expect(tabMatches).toHaveLength(3);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it("should have Account tab visibility", () => {
|
|
458
|
+
expect(profileContent).toMatch(/<tabVisibilities>[\s\S]*?<tab>standard-Account<\/tab>[\s\S]*?<visibility>Available<\/visibility>[\s\S]*?<\/tabVisibilities>/);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it("should have Opportunity tab visibility", () => {
|
|
462
|
+
expect(profileContent).toMatch(/<tabVisibilities>[\s\S]*?<tab>standard-Opportunity<\/tab>[\s\S]*?<visibility>Available<\/visibility>[\s\S]*?<\/tabVisibilities>/);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it("should have CustomObject__c tab visibility", () => {
|
|
466
|
+
expect(profileContent).toMatch(/<tabVisibilities>[\s\S]*?<tab>CustomObject__c<\/tab>[\s\S]*?<visibility>Available<\/visibility>[\s\S]*?<\/tabVisibilities>/);
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
describe("Record Type Visibilities", () => {
|
|
471
|
+
it("should have 2 recordTypeVisibilities entries", () => {
|
|
472
|
+
const recordTypeMatches = profileContent.match(/<recordTypeVisibilities>/g);
|
|
473
|
+
expect(recordTypeMatches).toHaveLength(2);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should have Customer_Account record type visibility", () => {
|
|
477
|
+
expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.Customer_Account<\/recordType>[\s\S]*?<default>true<\/default>[\s\S]*?<visible>true<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it("should have PersonAccount record type visibility", () => {
|
|
481
|
+
expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.PersonAccount<\/recordType>[\s\S]*?<default>false<\/default>[\s\S]*?<visible>false<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// =========================================================================
|
|
487
|
+
// PERMISSION SET METADATA (SalesRepresentative.permissionset-meta.xml)
|
|
488
|
+
// =========================================================================
|
|
489
|
+
|
|
490
|
+
describe("PermissionSet Metadata (SalesRepresentative.permissionset-meta.xml)", () => {
|
|
491
|
+
let permSetContent: string;
|
|
492
|
+
|
|
493
|
+
beforeAll(() => {
|
|
494
|
+
const permSetPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml");
|
|
495
|
+
permSetContent = fs.readFileSync(permSetPath, "utf-8");
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
describe("File Detection", () => {
|
|
499
|
+
it("should have correct XML declaration", () => {
|
|
500
|
+
expect(permSetContent).toContain('<?xml version="1.0" encoding="UTF-8"?>');
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it("should have PermissionSet as root element", () => {
|
|
504
|
+
expect(permSetContent).toContain("<PermissionSet xmlns=");
|
|
505
|
+
expect(permSetContent).toContain("http://salesforce.com/2006/04/metadata");
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it("should have custom=false", () => {
|
|
509
|
+
expect(permSetContent).toContain("<custom>false</custom>");
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
describe("Label and Description", () => {
|
|
514
|
+
it("should have label Sales Representative", () => {
|
|
515
|
+
expect(permSetContent).toContain("<label>Sales Representative</label>");
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("should have description", () => {
|
|
519
|
+
expect(permSetContent).toContain("<description>Permission set for Sales Representatives</description>");
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
describe("Field Permissions", () => {
|
|
524
|
+
it("should have 15 fieldPermissions entries", () => {
|
|
525
|
+
const fieldPermissionMatches = permSetContent.match(/<fieldPermissions>/g);
|
|
526
|
+
expect(fieldPermissionMatches).toHaveLength(15);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it("should have field permissions for Account standard fields", () => {
|
|
530
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Active__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
531
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.AnnualRevenue<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
532
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.BillingCity<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
533
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.BillingState<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
534
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Industry<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
535
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("should have field permissions for Opportunity standard fields", () => {
|
|
539
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Amount<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
540
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.CloseDate<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
541
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.Description<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
542
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Opportunity\.StageName<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it("should have field permissions for CustomObject__c fields", () => {
|
|
546
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Amount__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
547
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Category__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
548
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.IsActive__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
549
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Name__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
550
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>CustomObject__c\.Stage__c<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
describe("Object Permissions", () => {
|
|
555
|
+
it("should have 3 objectPermissions entries", () => {
|
|
556
|
+
const objectPermissionMatches = permSetContent.match(/<objectPermissions>/g);
|
|
557
|
+
expect(objectPermissionMatches).toHaveLength(3);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it("should have correct permissions for Account (limited)", () => {
|
|
561
|
+
expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<object>Account<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>false<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>false<\/modifyAllRecords>[\s\S]*?<viewAllRecords>false<\/viewAllRecords>[\s\S]*?<\/objectPermissions>/);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("should have correct permissions for Opportunity (limited)", () => {
|
|
565
|
+
expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<object>Opportunity<\/object>[\s\S]*?<allowCreate>true<\/allowCreate>[\s\S]*?<allowDelete>false<\/allowDelete>[\s\S]*?<allowEdit>true<\/allowEdit>[\s\S]*?<allowRead>true<\/allowRead>[\s\S]*?<modifyAllRecords>false<\/modifyAllRecords>[\s\S]*?<viewAllRecords>false<\/viewAllRecords>[\s\S]*?<\/objectPermissions>/);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it("should have correct permissions for CustomObject__c (limited)", () => {
|
|
569
|
+
const customObjPermMatch = permSetContent.match(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
|
|
570
|
+
expect(customObjPermMatch).toBeTruthy();
|
|
571
|
+
const customObjSection = customObjPermMatch?.[0] || "";
|
|
572
|
+
expect(customObjSection).toContain("<allowCreate>true</allowCreate>");
|
|
573
|
+
expect(customObjSection).toContain("<allowDelete>false</allowDelete>");
|
|
574
|
+
expect(customObjSection).toContain("<allowEdit>true</allowEdit>");
|
|
575
|
+
expect(customObjSection).toContain("<allowRead>true</allowRead>");
|
|
576
|
+
expect(customObjSection).toContain("<modifyAllRecords>false</modifyAllRecords>");
|
|
577
|
+
expect(customObjSection).toContain("<viewAllRecords>false</viewAllRecords>");
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
describe("Class Accesses", () => {
|
|
582
|
+
it("should have 1 classAccesses entry", () => {
|
|
583
|
+
const classAccessMatches = permSetContent.match(/<classAccesses>/g);
|
|
584
|
+
expect(classAccessMatches).toHaveLength(1);
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it("should enable AccountController", () => {
|
|
588
|
+
expect(permSetContent).toMatch(/<classAccesses>[\s\S]*?<apexClass>AccountController<\/apexClass>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/classAccesses>/);
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
describe("Flow Accesses", () => {
|
|
593
|
+
it("should have 1 flowAccesses entry", () => {
|
|
594
|
+
const flowAccessMatches = permSetContent.match(/<flowAccesses>/g);
|
|
595
|
+
expect(flowAccessMatches).toHaveLength(1);
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it("should enable Account_Create flow", () => {
|
|
599
|
+
expect(permSetContent).toMatch(/<flowAccesses>[\s\S]*?<flow>Account_Create<\/flow>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/flowAccesses>/);
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
describe("User Permissions", () => {
|
|
604
|
+
it("should have 2 userPermissions entries", () => {
|
|
605
|
+
const userPermMatches = permSetContent.match(/<userPermissions>/g);
|
|
606
|
+
expect(userPermMatches).toHaveLength(2);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it("should have ViewReadonlyFields permission enabled", () => {
|
|
610
|
+
expect(permSetContent).toMatch(/<userPermissions>[\s\S]*?<name>ViewReadonlyFields<\/name>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/userPermissions>/);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
it("should have AllowViewKnowledge permission enabled", () => {
|
|
614
|
+
expect(permSetContent).toMatch(/<userPermissions>[\s\S]*?<name>AllowViewKnowledge<\/name>[\s\S]*?<enabled>true<\/enabled>[\s\S]*?<\/userPermissions>/);
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
describe("hasActivationRequired", () => {
|
|
619
|
+
it("should have hasActivationRequired as false", () => {
|
|
620
|
+
expect(permSetContent).toContain("<hasActivationRequired>false</hasActivationRequired>");
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// =========================================================================
|
|
626
|
+
// FULL SFDX PROJECT INTEGRATION (END-TO-END)
|
|
627
|
+
// =========================================================================
|
|
628
|
+
|
|
629
|
+
describe("Full SFDX Project Integration (End-to-End)", () => {
|
|
630
|
+
|
|
631
|
+
describe("SFDX File Structure Completeness", () => {
|
|
632
|
+
it("should have all required SFDX files", () => {
|
|
633
|
+
const requiredFiles = [
|
|
634
|
+
"sfdx-project.json",
|
|
635
|
+
"force-app/main/default/package.xml",
|
|
636
|
+
"force-app/main/default/profiles/Admin.profile-meta.xml",
|
|
637
|
+
"force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml"
|
|
638
|
+
];
|
|
639
|
+
|
|
640
|
+
for (const file of requiredFiles) {
|
|
641
|
+
const filePath = path.join(SALESFORCE_ENTERPRISE_PATH, file);
|
|
642
|
+
expect(fs.existsSync(filePath)).toBe(true);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it("should have matching sourceApiVersion across SFDX files", () => {
|
|
647
|
+
const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
|
|
648
|
+
const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
|
|
649
|
+
|
|
650
|
+
const sfdxProjectContent = fs.readFileSync(sfdxProjectPath, "utf-8");
|
|
651
|
+
const packageXmlContent = fs.readFileSync(packageXmlPath, "utf-8");
|
|
652
|
+
|
|
653
|
+
const sfdxProjectJson = JSON.parse(sfdxProjectContent);
|
|
654
|
+
expect(sfdxProjectJson.sourceApiVersion).toBe("59.0");
|
|
655
|
+
expect(packageXmlContent).toContain("<version>59.0</version>");
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
describe("Component Cross-Reference Validation", () => {
|
|
660
|
+
let sfdxProjectContent: string;
|
|
661
|
+
let packageXmlContent: string;
|
|
662
|
+
let profileContent: string;
|
|
663
|
+
let permSetContent: string;
|
|
664
|
+
|
|
665
|
+
beforeAll(() => {
|
|
666
|
+
sfdxProjectContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json"), "utf-8");
|
|
667
|
+
packageXmlContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml"), "utf-8");
|
|
668
|
+
profileContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml"), "utf-8");
|
|
669
|
+
permSetContent = fs.readFileSync(path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml"), "utf-8");
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it("should reference Apex classes consistently", () => {
|
|
673
|
+
// package.xml lists ApexClass members
|
|
674
|
+
expect(packageXmlContent).toContain("<members>AccountController</members>");
|
|
675
|
+
|
|
676
|
+
// Profile enables AccountController
|
|
677
|
+
expect(profileContent).toContain("<apexClass>AccountController</apexClass>");
|
|
678
|
+
|
|
679
|
+
// PermissionSet enables AccountController
|
|
680
|
+
expect(permSetContent).toContain("<apexClass>AccountController</apexClass>");
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it("should reference flows consistently", () => {
|
|
684
|
+
// package.xml lists Flow members
|
|
685
|
+
expect(packageXmlContent).toContain("<members>Account_Create</members>");
|
|
686
|
+
expect(packageXmlContent).toContain("<members>Opportunity_Update</members>");
|
|
687
|
+
|
|
688
|
+
// Profile enables both flows
|
|
689
|
+
expect(profileContent).toContain("<flow>Account_Create</flow>");
|
|
690
|
+
expect(profileContent).toContain("<flow>Opportunity_Update</flow>");
|
|
691
|
+
|
|
692
|
+
// PermissionSet enables Account_Create
|
|
693
|
+
expect(permSetContent).toContain("<flow>Account_Create</flow>");
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it("should reference CustomObject__c consistently", () => {
|
|
697
|
+
// package.xml has CustomObject with CustomObject__c
|
|
698
|
+
expect(packageXmlContent).toContain("<members>CustomObject__c</members>");
|
|
699
|
+
|
|
700
|
+
// Profile has object permissions for CustomObject__c
|
|
701
|
+
expect(profileContent).toMatch(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
|
|
702
|
+
|
|
703
|
+
// PermissionSet has object permissions for CustomObject__c
|
|
704
|
+
expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<object>CustomObject__c<\/object>[\s\S]*?<\/objectPermissions>/);
|
|
705
|
+
|
|
706
|
+
// Profile has field permissions for CustomObject__c fields
|
|
707
|
+
expect(profileContent).toContain("<field>CustomObject__c.Amount__c</field>");
|
|
708
|
+
expect(profileContent).toContain("<field>CustomObject__c.Category__c</field>");
|
|
709
|
+
|
|
710
|
+
// PermissionSet has field permissions for CustomObject__c fields
|
|
711
|
+
expect(permSetContent).toContain("<field>CustomObject__c.Amount__c</field>");
|
|
712
|
+
expect(permSetContent).toContain("<field>CustomObject__c.Category__c</field>");
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it("should have Profile listed in package.xml", () => {
|
|
716
|
+
expect(packageXmlContent).toMatch(/<types>[\s\S]*?<members>Admin<\/members>[\s\S]*?<name>Profile<\/name>[\s\S]*?<\/types>/);
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it("should have PermissionSet listed in package.xml", () => {
|
|
720
|
+
expect(packageXmlContent).toMatch(/<types>[\s\S]*?<members>SalesRepresentative<\/members>[\s\S]*?<name>PermissionSet<\/name>[\s\S]*?<\/types>/);
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
describe("Metadata Component File Existence", () => {
|
|
725
|
+
it("should have all Apex class files", () => {
|
|
726
|
+
const apexClasses = [
|
|
727
|
+
"AccountController.cls",
|
|
728
|
+
"AccountControllerTest.cls",
|
|
729
|
+
"OpportunityBatch.cls",
|
|
730
|
+
"OpportunityBatchTest.cls",
|
|
731
|
+
"OpportunityTriggerHandler.cls",
|
|
732
|
+
"OpportunityTriggerHelper.cls",
|
|
733
|
+
"Logger.cls",
|
|
734
|
+
"TriggerManagement.cls"
|
|
735
|
+
];
|
|
736
|
+
|
|
737
|
+
for (const apexClass of apexClasses) {
|
|
738
|
+
const filePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes", apexClass);
|
|
739
|
+
expect(fs.existsSync(filePath)).toBe(true);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
it("should have Apex trigger file", () => {
|
|
744
|
+
const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
|
|
745
|
+
expect(fs.existsSync(triggerPath)).toBe(true);
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it("should have LWC component files", () => {
|
|
749
|
+
const lwcComponents = ["accountList", "opportunityCard"];
|
|
750
|
+
|
|
751
|
+
for (const component of lwcComponents) {
|
|
752
|
+
const componentDir = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/lwc", component);
|
|
753
|
+
expect(fs.existsSync(path.join(componentDir, `${component}.js`))).toBe(true);
|
|
754
|
+
expect(fs.existsSync(path.join(componentDir, `${component}.html`))).toBe(true);
|
|
755
|
+
expect(fs.existsSync(path.join(componentDir, `${component}.css`))).toBe(true);
|
|
756
|
+
expect(fs.existsSync(path.join(componentDir, `${component}.js-meta.xml`))).toBe(true);
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it("should have Flow metadata files", () => {
|
|
761
|
+
const flows = ["Account_Create.flow-meta.xml", "Opportunity_Update.flow-meta.xml"];
|
|
762
|
+
|
|
763
|
+
for (const flow of flows) {
|
|
764
|
+
const flowPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows", flow);
|
|
765
|
+
expect(fs.existsSync(flowPath)).toBe(true);
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
it("should have CustomObject metadata file", () => {
|
|
770
|
+
const objectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/objects/CustomObject__c/CustomObject__c.object-meta.xml");
|
|
771
|
+
expect(fs.existsSync(objectPath)).toBe(true);
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
describe("SFDX API Version Consistency", () => {
|
|
776
|
+
it("should have consistent API version across project", () => {
|
|
777
|
+
const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
|
|
778
|
+
const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
|
|
779
|
+
|
|
780
|
+
const sfdxProjectContent = fs.readFileSync(sfdxProjectPath, "utf-8");
|
|
781
|
+
const packageXmlContent = fs.readFileSync(packageXmlPath, "utf-8");
|
|
782
|
+
|
|
783
|
+
const sfdxProjectJson = JSON.parse(sfdxProjectContent);
|
|
784
|
+
const sourceApiVersion = sfdxProjectJson.sourceApiVersion;
|
|
785
|
+
|
|
786
|
+
// package.xml version
|
|
787
|
+
expect(packageXmlContent).toContain(`<version>${sourceApiVersion}</version>`);
|
|
788
|
+
|
|
789
|
+
// Flow API versions
|
|
790
|
+
const accountCreateFlowPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows/Account_Create.flow-meta.xml");
|
|
791
|
+
const opportunityUpdateFlowPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/flows/Opportunity_Update.flow-meta.xml");
|
|
792
|
+
const accountCreateFlowContent = fs.readFileSync(accountCreateFlowPath, "utf-8");
|
|
793
|
+
const opportunityUpdateFlowContent = fs.readFileSync(opportunityUpdateFlowPath, "utf-8");
|
|
794
|
+
|
|
795
|
+
expect(accountCreateFlowContent).toContain(`<apiVersion>${sourceApiVersion}</apiVersion>`);
|
|
796
|
+
expect(opportunityUpdateFlowContent).toContain(`<apiVersion>${sourceApiVersion}</apiVersion>`);
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
// =========================================================================
|
|
802
|
+
// UNIT TESTS - SFDX Metadata XML parsing patterns
|
|
803
|
+
// =========================================================================
|
|
804
|
+
|
|
805
|
+
describe("Unit: SFDX Metadata XML parsing patterns", () => {
|
|
806
|
+
|
|
807
|
+
describe("sfdx-project.json parsing", () => {
|
|
808
|
+
it("should parse valid sfdx-project.json structure", () => {
|
|
809
|
+
const sfdxJson = {
|
|
810
|
+
packageDirectories: [{
|
|
811
|
+
path: "force-app",
|
|
812
|
+
default: true,
|
|
813
|
+
package: "TestPackage",
|
|
814
|
+
versionName: "ver 1.0",
|
|
815
|
+
versionNumber: "1.0.0.NEXT"
|
|
816
|
+
}],
|
|
817
|
+
name: "TestProject",
|
|
818
|
+
namespace: "",
|
|
819
|
+
sfdcLoginUrl: "https://login.salesforce.com",
|
|
820
|
+
singlePackage: false,
|
|
821
|
+
sourceApiVersion: "59.0"
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
expect(sfdxJson.packageDirectories[0].path).toBe("force-app");
|
|
825
|
+
expect(sfdxJson.packageDirectories[0].default).toBe(true);
|
|
826
|
+
expect(sfdxJson.name).toBe("TestProject");
|
|
827
|
+
expect(sfdxJson.sourceApiVersion).toBe("59.0");
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
it("should handle namespace correctly", () => {
|
|
831
|
+
const sfdxJson = {
|
|
832
|
+
namespace: "my_namespace"
|
|
833
|
+
};
|
|
834
|
+
expect(sfdxJson.namespace).toBe("my_namespace");
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it("should handle empty namespace", () => {
|
|
838
|
+
const sfdxJson = {
|
|
839
|
+
namespace: ""
|
|
840
|
+
};
|
|
841
|
+
expect(sfdxJson.namespace).toBe("");
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
describe("package.xml parsing", () => {
|
|
846
|
+
it("should parse package.xml type structure", () => {
|
|
847
|
+
const packageXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
848
|
+
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
849
|
+
<types>
|
|
850
|
+
<members>TestClass</members>
|
|
851
|
+
<name>ApexClass</name>
|
|
852
|
+
</types>
|
|
853
|
+
<version>59.0</version>
|
|
854
|
+
</Package>`;
|
|
855
|
+
|
|
856
|
+
expect(packageXml).toContain("<name>ApexClass</name>");
|
|
857
|
+
expect(packageXml).toContain("<members>TestClass</members>");
|
|
858
|
+
expect(packageXml).toContain("<version>59.0</version>");
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
it("should parse multiple type members", () => {
|
|
862
|
+
const packageXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
863
|
+
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
864
|
+
<types>
|
|
865
|
+
<members>Class1</members>
|
|
866
|
+
<members>Class2</members>
|
|
867
|
+
<members>Class3</members>
|
|
868
|
+
<name>ApexClass</name>
|
|
869
|
+
</types>
|
|
870
|
+
</Package>`;
|
|
871
|
+
|
|
872
|
+
const memberMatches = packageXml.match(/<members>\w+<\/members>/g);
|
|
873
|
+
expect(memberMatches).toHaveLength(3);
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
describe("Profile XML parsing", () => {
|
|
878
|
+
it("should parse fieldPermissions structure", () => {
|
|
879
|
+
const profileXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
880
|
+
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
881
|
+
<fieldPermissions>
|
|
882
|
+
<field>Account.Name</field>
|
|
883
|
+
<readable>true</readable>
|
|
884
|
+
<editable>true</editable>
|
|
885
|
+
</fieldPermissions>
|
|
886
|
+
</Profile>`;
|
|
887
|
+
|
|
888
|
+
expect(profileXml).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
889
|
+
expect(profileXml).toContain("<readable>true</readable>");
|
|
890
|
+
expect(profileXml).toContain("<editable>true</editable>");
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it("should parse objectPermissions structure", () => {
|
|
894
|
+
const profileXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
895
|
+
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
896
|
+
<objectPermissions>
|
|
897
|
+
<allowCreate>true</allowCreate>
|
|
898
|
+
<allowDelete>true</allowDelete>
|
|
899
|
+
<allowEdit>true</allowEdit>
|
|
900
|
+
<allowRead>true</allowRead>
|
|
901
|
+
<modifyAllRecords>true</modifyAllRecords>
|
|
902
|
+
<object>Account</object>
|
|
903
|
+
</objectPermissions>
|
|
904
|
+
</Profile>`;
|
|
905
|
+
|
|
906
|
+
expect(profileXml).toMatch(/<objectPermissions>[\s\S]*?<object>Account<\/object>[\s\S]*?<\/objectPermissions>/);
|
|
907
|
+
expect(profileXml).toContain("<allowCreate>true</allowCreate>");
|
|
908
|
+
expect(profileXml).toContain("<modifyAllRecords>true</modifyAllRecords>");
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
it("should parse classAccesses structure", () => {
|
|
912
|
+
const profileXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
913
|
+
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
914
|
+
<classAccesses>
|
|
915
|
+
<apexClass>TestClass</apexClass>
|
|
916
|
+
<enabled>true</enabled>
|
|
917
|
+
</classAccesses>
|
|
918
|
+
</Profile>`;
|
|
919
|
+
|
|
920
|
+
expect(profileXml).toMatch(/<classAccesses>[\s\S]*?<apexClass>TestClass<\/apexClass>[\s\S]*?<\/classAccesses>/);
|
|
921
|
+
expect(profileXml).toContain("<enabled>true</enabled>");
|
|
922
|
+
});
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
describe("PermissionSet XML parsing", () => {
|
|
926
|
+
it("should parse PermissionSet basic structure", () => {
|
|
927
|
+
const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
928
|
+
<PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
|
|
929
|
+
<custom>false</custom>
|
|
930
|
+
<label>Test Permission Set</label>
|
|
931
|
+
<description>Test description</description>
|
|
932
|
+
</PermissionSet>`;
|
|
933
|
+
|
|
934
|
+
expect(permSetXml).toContain("<label>Test Permission Set</label>");
|
|
935
|
+
expect(permSetXml).toContain("<description>Test description</description>");
|
|
936
|
+
expect(permSetXml).toContain("<custom>false</custom>");
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
it("should parse fieldPermissions with readable and editable", () => {
|
|
940
|
+
const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
941
|
+
<PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
|
|
942
|
+
<fieldPermissions>
|
|
943
|
+
<field>Account.Name</field>
|
|
944
|
+
<readable>true</readable>
|
|
945
|
+
<editable>false</editable>
|
|
946
|
+
</fieldPermissions>
|
|
947
|
+
</PermissionSet>`;
|
|
948
|
+
|
|
949
|
+
expect(permSetXml).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<\/fieldPermissions>/);
|
|
950
|
+
expect(permSetXml).toContain("<readable>true</readable>");
|
|
951
|
+
expect(permSetXml).toContain("<editable>false</editable>");
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
it("should parse objectPermissions with viewAllRecords and modifyAllRecords", () => {
|
|
955
|
+
const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
956
|
+
<PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
|
|
957
|
+
<objectPermissions>
|
|
958
|
+
<allowCreate>true</allowCreate>
|
|
959
|
+
<allowEdit>true</allowEdit>
|
|
960
|
+
<allowRead>true</allowRead>
|
|
961
|
+
<viewAllRecords>false</viewAllRecords>
|
|
962
|
+
<modifyAllRecords>false</modifyAllRecords>
|
|
963
|
+
<object>Account</object>
|
|
964
|
+
</objectPermissions>
|
|
965
|
+
</PermissionSet>`;
|
|
966
|
+
|
|
967
|
+
expect(permSetXml).toContain("<viewAllRecords>false</viewAllRecords>");
|
|
968
|
+
expect(permSetXml).toContain("<modifyAllRecords>false</modifyAllRecords>");
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it("should parse userPermissions", () => {
|
|
972
|
+
const permSetXml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
973
|
+
<PermissionSet xmlns="http://salesforce.com/2006/04/metadata">
|
|
974
|
+
<userPermissions>
|
|
975
|
+
<name>ViewReadonlyFields</name>
|
|
976
|
+
<enabled>true</enabled>
|
|
977
|
+
</userPermissions>
|
|
978
|
+
</PermissionSet>`;
|
|
979
|
+
|
|
980
|
+
expect(permSetXml).toMatch(/<userPermissions>[\s\S]*?<name>ViewReadonlyFields<\/name>[\s\S]*?<\/userPermissions>/);
|
|
981
|
+
expect(permSetXml).toContain("<enabled>true</enabled>");
|
|
982
|
+
});
|
|
983
|
+
});
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
// =========================================================================
|
|
987
|
+
// EDGE CASES
|
|
988
|
+
// =========================================================================
|
|
989
|
+
|
|
990
|
+
describe("Edge Cases", () => {
|
|
991
|
+
|
|
992
|
+
describe("SFDX Project Edge Cases", () => {
|
|
993
|
+
it("should handle empty namespace", () => {
|
|
994
|
+
const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
|
|
995
|
+
const content = fs.readFileSync(sfdxProjectPath, "utf-8");
|
|
996
|
+
const json = JSON.parse(content);
|
|
997
|
+
expect(json.namespace).toBe("");
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
it("should handle singlePackage=false", () => {
|
|
1001
|
+
const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
|
|
1002
|
+
const content = fs.readFileSync(sfdxProjectPath, "utf-8");
|
|
1003
|
+
const json = JSON.parse(content);
|
|
1004
|
+
expect(json.singlePackage).toBe(false);
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
it("should handle versionNumber with NEXT", () => {
|
|
1008
|
+
const sfdxProjectPath = path.join(SALESFORCE_ENTERPRISE_PATH, "sfdx-project.json");
|
|
1009
|
+
const content = fs.readFileSync(sfdxProjectPath, "utf-8");
|
|
1010
|
+
const json = JSON.parse(content);
|
|
1011
|
+
expect(json.packageDirectories[0].versionNumber).toBe("1.0.0.NEXT");
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
describe("Profile Edge Cases", () => {
|
|
1016
|
+
let profileContent: string;
|
|
1017
|
+
|
|
1018
|
+
beforeAll(() => {
|
|
1019
|
+
const profilePath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/profiles/Admin.profile-meta.xml");
|
|
1020
|
+
profileContent = fs.readFileSync(profilePath, "utf-8");
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
it("should handle read-only field permissions", () => {
|
|
1024
|
+
// Account.Total_Opportunity_Amount__c has readable=true, editable=false
|
|
1025
|
+
expect(profileContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Total_Opportunity_Amount__c<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>false<\/editable>[\s\S]*?<\/fieldPermissions>/);
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
it("should handle multiple layoutAssignments", () => {
|
|
1029
|
+
const layoutMatches = profileContent.match(/<layoutAssignments>[\s\S]*?<\/layoutAssignments>/g);
|
|
1030
|
+
expect(layoutMatches).toHaveLength(3);
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it("should handle recordTypeVisibilities with different default/visible settings", () => {
|
|
1034
|
+
// Customer_Account: default=true, visible=true
|
|
1035
|
+
// PersonAccount: default=false, visible=false
|
|
1036
|
+
expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.Customer_Account<\/recordType>[\s\S]*?<default>true<\/default>[\s\S]*?<visible>true<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
|
|
1037
|
+
expect(profileContent).toMatch(/<recordTypeVisibilities>[\s\S]*?<recordType>Account\.PersonAccount<\/recordType>[\s\S]*?<default>false<\/default>[\s\S]*?<visible>false<\/visible>[\s\S]*?<\/recordTypeVisibilities>/);
|
|
1038
|
+
});
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
describe("PermissionSet Edge Cases", () => {
|
|
1042
|
+
let permSetContent: string;
|
|
1043
|
+
|
|
1044
|
+
beforeAll(() => {
|
|
1045
|
+
const permSetPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/permissionsets/SalesRepresentative.permissionset-meta.xml");
|
|
1046
|
+
permSetContent = fs.readFileSync(permSetPath, "utf-8");
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
it("should handle hasActivationRequired=false", () => {
|
|
1050
|
+
expect(permSetContent).toContain("<hasActivationRequired>false</hasActivationRequired>");
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
it("should handle limited object permissions", () => {
|
|
1054
|
+
// PermissionSet has allowCreate=true, allowDelete=false, allowEdit=true, allowRead=true
|
|
1055
|
+
// modifyAllRecords=false, viewAllRecords=false
|
|
1056
|
+
expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<allowDelete>false<\/allowDelete>[\s\S]*?<\/objectPermissions>/);
|
|
1057
|
+
expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<modifyAllRecords>false<\/modifyAllRecords>[\s\S]*?<\/objectPermissions>/);
|
|
1058
|
+
expect(permSetContent).toMatch(/<objectPermissions>[\s\S]*?<viewAllRecords>false<\/viewAllRecords>[\s\S]*?<\/objectPermissions>/);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
it("should handle multiple field permissions with different editable settings", () => {
|
|
1062
|
+
// Account.AnnualRevenue: readable=true, editable=false
|
|
1063
|
+
// Account.Name: readable=true, editable=true
|
|
1064
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.AnnualRevenue<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>false<\/editable>[\s\S]*?<\/fieldPermissions>/);
|
|
1065
|
+
expect(permSetContent).toMatch(/<fieldPermissions>[\s\S]*?<field>Account\.Name<\/field>[\s\S]*?<readable>true<\/readable>[\s\S]*?<editable>true<\/editable>[\s\S]*?<\/fieldPermissions>/);
|
|
1066
|
+
});
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
describe("Package.xml Edge Cases", () => {
|
|
1070
|
+
it("should handle mixed component types", () => {
|
|
1071
|
+
const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
|
|
1072
|
+
const content = fs.readFileSync(packageXmlPath, "utf-8");
|
|
1073
|
+
|
|
1074
|
+
// 7 different type elements
|
|
1075
|
+
const typeNames = content.match(/<name>\w+<\/name>/g);
|
|
1076
|
+
expect(typeNames).toHaveLength(7);
|
|
1077
|
+
|
|
1078
|
+
// All types should be unique
|
|
1079
|
+
const uniqueTypes = [...new Set(typeNames)];
|
|
1080
|
+
expect(uniqueTypes).toHaveLength(7);
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
it("should handle types with single member", () => {
|
|
1084
|
+
const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
|
|
1085
|
+
const content = fs.readFileSync(packageXmlPath, "utf-8");
|
|
1086
|
+
|
|
1087
|
+
// ApexTrigger has 1 member
|
|
1088
|
+
expect(content).toMatch(/<members>OpportunityTrigger<\/members>[\s\S]*?<name>ApexTrigger<\/name>[\s\S]*?<\/types>/);
|
|
1089
|
+
|
|
1090
|
+
// CustomObject has 1 member
|
|
1091
|
+
expect(content).toMatch(/<members>CustomObject__c<\/members>[\s\S]*?<name>CustomObject<\/name>[\s\S]*?<\/types>/);
|
|
1092
|
+
|
|
1093
|
+
// Profile has 1 member
|
|
1094
|
+
expect(content).toMatch(/<members>Admin<\/members>[\s\S]*?<name>Profile<\/name>[\s\S]*?<\/types>/);
|
|
1095
|
+
|
|
1096
|
+
// PermissionSet has 1 member
|
|
1097
|
+
expect(content).toMatch(/<members>SalesRepresentative<\/members>[\s\S]*?<name>PermissionSet<\/name>[\s\S]*?<\/types>/);
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
it("should handle types with multiple members", () => {
|
|
1101
|
+
const packageXmlPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/package.xml");
|
|
1102
|
+
const content = fs.readFileSync(packageXmlPath, "utf-8");
|
|
1103
|
+
|
|
1104
|
+
// ApexClass has 9 members - extract by finding the section between types tags
|
|
1105
|
+
const apexClassSection = content.substring(content.indexOf("<members>AccountController</members>"), content.indexOf("</types>"));
|
|
1106
|
+
const apexMembers = apexClassSection.match(/<members>\w+<\/members>/g);
|
|
1107
|
+
expect(apexMembers).toHaveLength(9);
|
|
1108
|
+
|
|
1109
|
+
// LightningComponentBundle has 2 members
|
|
1110
|
+
const lwcStart = content.indexOf("<members>accountList</members>");
|
|
1111
|
+
const lwcEnd = content.indexOf("</types>", lwcStart);
|
|
1112
|
+
const lwcSection = content.substring(lwcStart - 100, lwcEnd);
|
|
1113
|
+
const lwcMembers = lwcSection.match(/<members>\w+<\/members>/g);
|
|
1114
|
+
expect(lwcMembers).toHaveLength(2);
|
|
1115
|
+
|
|
1116
|
+
// Flow has 2 members
|
|
1117
|
+
const flowStart = content.indexOf("<members>Account_Create</members>");
|
|
1118
|
+
const flowEnd = content.indexOf("</types>", flowStart);
|
|
1119
|
+
const flowSection = content.substring(flowStart - 100, flowEnd);
|
|
1120
|
+
const flowMembers = flowSection.match(/<members>\w+<\/members>/g);
|
|
1121
|
+
expect(flowMembers).toHaveLength(2);
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
});
|
|
1125
|
+
});
|