solokit 0.1.1__py3-none-any.whl
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.
- solokit/__init__.py +10 -0
- solokit/__version__.py +3 -0
- solokit/cli.py +374 -0
- solokit/core/__init__.py +1 -0
- solokit/core/cache.py +102 -0
- solokit/core/command_runner.py +278 -0
- solokit/core/config.py +453 -0
- solokit/core/config_validator.py +204 -0
- solokit/core/constants.py +291 -0
- solokit/core/error_formatter.py +279 -0
- solokit/core/error_handlers.py +346 -0
- solokit/core/exceptions.py +1567 -0
- solokit/core/file_ops.py +309 -0
- solokit/core/logging_config.py +166 -0
- solokit/core/output.py +99 -0
- solokit/core/performance.py +57 -0
- solokit/core/protocols.py +141 -0
- solokit/core/types.py +312 -0
- solokit/deployment/__init__.py +1 -0
- solokit/deployment/executor.py +411 -0
- solokit/git/__init__.py +1 -0
- solokit/git/integration.py +619 -0
- solokit/init/__init__.py +41 -0
- solokit/init/claude_commands_installer.py +87 -0
- solokit/init/dependency_installer.py +313 -0
- solokit/init/docs_structure.py +90 -0
- solokit/init/env_generator.py +160 -0
- solokit/init/environment_validator.py +334 -0
- solokit/init/git_hooks_installer.py +71 -0
- solokit/init/git_setup.py +188 -0
- solokit/init/gitignore_updater.py +195 -0
- solokit/init/initial_commit.py +145 -0
- solokit/init/initial_scans.py +109 -0
- solokit/init/orchestrator.py +246 -0
- solokit/init/readme_generator.py +207 -0
- solokit/init/session_structure.py +239 -0
- solokit/init/template_installer.py +424 -0
- solokit/learning/__init__.py +1 -0
- solokit/learning/archiver.py +115 -0
- solokit/learning/categorizer.py +126 -0
- solokit/learning/curator.py +428 -0
- solokit/learning/extractor.py +352 -0
- solokit/learning/reporter.py +351 -0
- solokit/learning/repository.py +254 -0
- solokit/learning/similarity.py +342 -0
- solokit/learning/validator.py +144 -0
- solokit/project/__init__.py +1 -0
- solokit/project/init.py +1162 -0
- solokit/project/stack.py +436 -0
- solokit/project/sync_plugin.py +438 -0
- solokit/project/tree.py +375 -0
- solokit/quality/__init__.py +1 -0
- solokit/quality/api_validator.py +424 -0
- solokit/quality/checkers/__init__.py +25 -0
- solokit/quality/checkers/base.py +114 -0
- solokit/quality/checkers/context7.py +221 -0
- solokit/quality/checkers/custom.py +162 -0
- solokit/quality/checkers/deployment.py +323 -0
- solokit/quality/checkers/documentation.py +179 -0
- solokit/quality/checkers/formatting.py +161 -0
- solokit/quality/checkers/integration.py +394 -0
- solokit/quality/checkers/linting.py +159 -0
- solokit/quality/checkers/security.py +261 -0
- solokit/quality/checkers/spec_completeness.py +127 -0
- solokit/quality/checkers/tests.py +184 -0
- solokit/quality/env_validator.py +306 -0
- solokit/quality/gates.py +655 -0
- solokit/quality/reporters/__init__.py +10 -0
- solokit/quality/reporters/base.py +25 -0
- solokit/quality/reporters/console.py +98 -0
- solokit/quality/reporters/json_reporter.py +34 -0
- solokit/quality/results.py +98 -0
- solokit/session/__init__.py +1 -0
- solokit/session/briefing/__init__.py +245 -0
- solokit/session/briefing/documentation_loader.py +53 -0
- solokit/session/briefing/formatter.py +476 -0
- solokit/session/briefing/git_context.py +282 -0
- solokit/session/briefing/learning_loader.py +212 -0
- solokit/session/briefing/milestone_builder.py +78 -0
- solokit/session/briefing/orchestrator.py +137 -0
- solokit/session/briefing/stack_detector.py +51 -0
- solokit/session/briefing/tree_generator.py +52 -0
- solokit/session/briefing/work_item_loader.py +209 -0
- solokit/session/briefing.py +353 -0
- solokit/session/complete.py +1188 -0
- solokit/session/status.py +246 -0
- solokit/session/validate.py +452 -0
- solokit/templates/.claude/commands/end.md +109 -0
- solokit/templates/.claude/commands/init.md +159 -0
- solokit/templates/.claude/commands/learn-curate.md +88 -0
- solokit/templates/.claude/commands/learn-search.md +62 -0
- solokit/templates/.claude/commands/learn-show.md +69 -0
- solokit/templates/.claude/commands/learn.md +136 -0
- solokit/templates/.claude/commands/start.md +114 -0
- solokit/templates/.claude/commands/status.md +22 -0
- solokit/templates/.claude/commands/validate.md +27 -0
- solokit/templates/.claude/commands/work-delete.md +119 -0
- solokit/templates/.claude/commands/work-graph.md +139 -0
- solokit/templates/.claude/commands/work-list.md +26 -0
- solokit/templates/.claude/commands/work-new.md +114 -0
- solokit/templates/.claude/commands/work-next.md +25 -0
- solokit/templates/.claude/commands/work-show.md +24 -0
- solokit/templates/.claude/commands/work-update.md +141 -0
- solokit/templates/CHANGELOG.md +17 -0
- solokit/templates/WORK_ITEM_TYPES.md +141 -0
- solokit/templates/__init__.py +1 -0
- solokit/templates/bug_spec.md +217 -0
- solokit/templates/config.schema.json +150 -0
- solokit/templates/dashboard_refine/base/.gitignore +36 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/layout.tsx +22 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/page.tsx +68 -0
- solokit/templates/dashboard_refine/base/app/(dashboard)/users/page.tsx +77 -0
- solokit/templates/dashboard_refine/base/app/globals.css +60 -0
- solokit/templates/dashboard_refine/base/app/layout.tsx +23 -0
- solokit/templates/dashboard_refine/base/app/page.tsx +9 -0
- solokit/templates/dashboard_refine/base/components/client-refine-wrapper.tsx +21 -0
- solokit/templates/dashboard_refine/base/components/layout/header.tsx +44 -0
- solokit/templates/dashboard_refine/base/components/layout/sidebar.tsx +82 -0
- solokit/templates/dashboard_refine/base/components/ui/button.tsx +53 -0
- solokit/templates/dashboard_refine/base/components/ui/card.tsx +78 -0
- solokit/templates/dashboard_refine/base/components/ui/table.tsx +116 -0
- solokit/templates/dashboard_refine/base/components.json +16 -0
- solokit/templates/dashboard_refine/base/lib/refine.tsx +65 -0
- solokit/templates/dashboard_refine/base/lib/utils.ts +13 -0
- solokit/templates/dashboard_refine/base/next.config.ts +10 -0
- solokit/templates/dashboard_refine/base/package.json.template +40 -0
- solokit/templates/dashboard_refine/base/postcss.config.mjs +8 -0
- solokit/templates/dashboard_refine/base/providers/refine-provider.tsx +26 -0
- solokit/templates/dashboard_refine/base/tailwind.config.ts +57 -0
- solokit/templates/dashboard_refine/base/tsconfig.json +27 -0
- solokit/templates/dashboard_refine/docker/Dockerfile +57 -0
- solokit/templates/dashboard_refine/docker/docker-compose.prod.yml +31 -0
- solokit/templates/dashboard_refine/docker/docker-compose.yml +21 -0
- solokit/templates/dashboard_refine/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/dashboard_refine/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/dashboard_refine/tier-1-essential/package.json.tier1.template +57 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/setup.ts +26 -0
- solokit/templates/dashboard_refine/tier-1-essential/tests/unit/example.test.tsx +73 -0
- solokit/templates/dashboard_refine/tier-2-standard/package.json.tier2.template +62 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/eslint.config.mjs +22 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/package.json.tier3.template +79 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/stryker.conf.json +38 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/dashboard.spec.ts +88 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/e2e/user-management.spec.ts +102 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/tests/integration/dashboard.test.tsx +90 -0
- solokit/templates/dashboard_refine/tier-3-comprehensive/type-coverage.json +16 -0
- solokit/templates/dashboard_refine/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/dashboard_refine/tier-4-production/k6/dashboard-load-test.js +70 -0
- solokit/templates/dashboard_refine/tier-4-production/next.config.ts +46 -0
- solokit/templates/dashboard_refine/tier-4-production/package.json.tier4.template +89 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/dashboard_refine/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/deployment_spec.md +500 -0
- solokit/templates/feature_spec.md +248 -0
- solokit/templates/fullstack_nextjs/base/.gitignore +36 -0
- solokit/templates/fullstack_nextjs/base/app/api/example/route.ts +65 -0
- solokit/templates/fullstack_nextjs/base/app/globals.css +27 -0
- solokit/templates/fullstack_nextjs/base/app/layout.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/app/page.tsx +32 -0
- solokit/templates/fullstack_nextjs/base/components/example-component.tsx +20 -0
- solokit/templates/fullstack_nextjs/base/lib/prisma.ts +17 -0
- solokit/templates/fullstack_nextjs/base/lib/utils.ts +13 -0
- solokit/templates/fullstack_nextjs/base/lib/validations.ts +20 -0
- solokit/templates/fullstack_nextjs/base/next.config.ts +7 -0
- solokit/templates/fullstack_nextjs/base/package.json.template +32 -0
- solokit/templates/fullstack_nextjs/base/postcss.config.mjs +8 -0
- solokit/templates/fullstack_nextjs/base/prisma/schema.prisma +21 -0
- solokit/templates/fullstack_nextjs/base/tailwind.config.ts +19 -0
- solokit/templates/fullstack_nextjs/base/tsconfig.json +27 -0
- solokit/templates/fullstack_nextjs/docker/Dockerfile +60 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.prod.yml +57 -0
- solokit/templates/fullstack_nextjs/docker/docker-compose.yml +47 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/package.json.tier1.template +48 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/api/example.test.ts +88 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/fullstack_nextjs/tier-1-essential/tests/unit/example.test.tsx +22 -0
- solokit/templates/fullstack_nextjs/tier-2-standard/package.json.tier2.template +52 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/package.json.tier3.template +68 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/stryker.conf.json +33 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/e2e/flow.spec.ts +59 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/tests/integration/api.test.ts +165 -0
- solokit/templates/fullstack_nextjs/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/fullstack_nextjs/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/fullstack_nextjs/tier-4-production/k6/load-test.js +45 -0
- solokit/templates/fullstack_nextjs/tier-4-production/next.config.ts +46 -0
- solokit/templates/fullstack_nextjs/tier-4-production/package.json.tier4.template +77 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/fullstack_nextjs/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/git-hooks/prepare-commit-msg +24 -0
- solokit/templates/integration_test_spec.md +363 -0
- solokit/templates/learnings.json +15 -0
- solokit/templates/ml_ai_fastapi/base/.gitignore +104 -0
- solokit/templates/ml_ai_fastapi/base/alembic/env.py +96 -0
- solokit/templates/ml_ai_fastapi/base/alembic.ini +114 -0
- solokit/templates/ml_ai_fastapi/base/pyproject.toml.template +91 -0
- solokit/templates/ml_ai_fastapi/base/requirements.txt.template +28 -0
- solokit/templates/ml_ai_fastapi/base/src/__init__.py +5 -0
- solokit/templates/ml_ai_fastapi/base/src/api/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/dependencies.py +20 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/example.py +134 -0
- solokit/templates/ml_ai_fastapi/base/src/api/routes/health.py +66 -0
- solokit/templates/ml_ai_fastapi/base/src/core/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/core/config.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/core/database.py +50 -0
- solokit/templates/ml_ai_fastapi/base/src/main.py +64 -0
- solokit/templates/ml_ai_fastapi/base/src/models/__init__.py +7 -0
- solokit/templates/ml_ai_fastapi/base/src/models/example.py +61 -0
- solokit/templates/ml_ai_fastapi/base/src/services/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/base/src/services/example.py +115 -0
- solokit/templates/ml_ai_fastapi/docker/Dockerfile +59 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.prod.yml +112 -0
- solokit/templates/ml_ai_fastapi/docker/docker-compose.yml +77 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyproject.toml.tier1.template +112 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pyrightconfig.json +41 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/pytest.ini +69 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/requirements-dev.txt +17 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/ruff.toml +81 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/conftest.py +72 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/test_main.py +49 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-1-essential/tests/unit/test_example.py +113 -0
- solokit/templates/ml_ai_fastapi/tier-2-standard/pyproject.toml.tier2.template +130 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/locustfile.py +99 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/mutmut_config.py +53 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/pyproject.toml.tier3.template +150 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/conftest.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-3-comprehensive/tests/integration/test_api.py +131 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/pyproject.toml.tier4.template +162 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/requirements-prod.txt +25 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/api/routes/metrics.py +19 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/logging.py +74 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/monitoring.py +68 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/core/sentry.py +66 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/__init__.py +3 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/logging.py +79 -0
- solokit/templates/ml_ai_fastapi/tier-4-production/src/middleware/tracing.py +60 -0
- solokit/templates/refactor_spec.md +287 -0
- solokit/templates/saas_t3/base/.gitignore +36 -0
- solokit/templates/saas_t3/base/app/api/trpc/[trpc]/route.ts +33 -0
- solokit/templates/saas_t3/base/app/globals.css +27 -0
- solokit/templates/saas_t3/base/app/layout.tsx +23 -0
- solokit/templates/saas_t3/base/app/page.tsx +31 -0
- solokit/templates/saas_t3/base/lib/api.tsx +77 -0
- solokit/templates/saas_t3/base/lib/utils.ts +13 -0
- solokit/templates/saas_t3/base/next.config.ts +7 -0
- solokit/templates/saas_t3/base/package.json.template +38 -0
- solokit/templates/saas_t3/base/postcss.config.mjs +8 -0
- solokit/templates/saas_t3/base/prisma/schema.prisma +20 -0
- solokit/templates/saas_t3/base/server/api/root.ts +19 -0
- solokit/templates/saas_t3/base/server/api/routers/example.ts +28 -0
- solokit/templates/saas_t3/base/server/api/trpc.ts +52 -0
- solokit/templates/saas_t3/base/server/db.ts +17 -0
- solokit/templates/saas_t3/base/tailwind.config.ts +19 -0
- solokit/templates/saas_t3/base/tsconfig.json +27 -0
- solokit/templates/saas_t3/docker/Dockerfile +60 -0
- solokit/templates/saas_t3/docker/docker-compose.prod.yml +59 -0
- solokit/templates/saas_t3/docker/docker-compose.yml +49 -0
- solokit/templates/saas_t3/tier-1-essential/.eslintrc.json +7 -0
- solokit/templates/saas_t3/tier-1-essential/jest.config.ts +17 -0
- solokit/templates/saas_t3/tier-1-essential/jest.setup.ts +1 -0
- solokit/templates/saas_t3/tier-1-essential/package.json.tier1.template +54 -0
- solokit/templates/saas_t3/tier-1-essential/tests/setup.ts +22 -0
- solokit/templates/saas_t3/tier-1-essential/tests/unit/example.test.tsx +24 -0
- solokit/templates/saas_t3/tier-2-standard/package.json.tier2.template +58 -0
- solokit/templates/saas_t3/tier-3-comprehensive/eslint.config.mjs +39 -0
- solokit/templates/saas_t3/tier-3-comprehensive/package.json.tier3.template +74 -0
- solokit/templates/saas_t3/tier-3-comprehensive/playwright.config.ts +66 -0
- solokit/templates/saas_t3/tier-3-comprehensive/stryker.conf.json +34 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/e2e/home.spec.ts +41 -0
- solokit/templates/saas_t3/tier-3-comprehensive/tests/integration/api.test.ts +44 -0
- solokit/templates/saas_t3/tier-3-comprehensive/type-coverage.json +12 -0
- solokit/templates/saas_t3/tier-4-production/instrumentation.ts +9 -0
- solokit/templates/saas_t3/tier-4-production/k6/load-test.js +51 -0
- solokit/templates/saas_t3/tier-4-production/next.config.ts +46 -0
- solokit/templates/saas_t3/tier-4-production/package.json.tier4.template +83 -0
- solokit/templates/saas_t3/tier-4-production/sentry.client.config.ts +26 -0
- solokit/templates/saas_t3/tier-4-production/sentry.edge.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/sentry.server.config.ts +11 -0
- solokit/templates/saas_t3/tier-4-production/vercel.json +37 -0
- solokit/templates/security_spec.md +287 -0
- solokit/templates/stack-versions.yaml +617 -0
- solokit/templates/status_update.json +6 -0
- solokit/templates/template-registry.json +257 -0
- solokit/templates/work_items.json +11 -0
- solokit/testing/__init__.py +1 -0
- solokit/testing/integration_runner.py +550 -0
- solokit/testing/performance.py +637 -0
- solokit/visualization/__init__.py +1 -0
- solokit/visualization/dependency_graph.py +788 -0
- solokit/work_items/__init__.py +1 -0
- solokit/work_items/creator.py +217 -0
- solokit/work_items/delete.py +264 -0
- solokit/work_items/get_dependencies.py +185 -0
- solokit/work_items/get_dependents.py +113 -0
- solokit/work_items/get_metadata.py +121 -0
- solokit/work_items/get_next_recommendations.py +133 -0
- solokit/work_items/manager.py +235 -0
- solokit/work_items/milestones.py +137 -0
- solokit/work_items/query.py +376 -0
- solokit/work_items/repository.py +267 -0
- solokit/work_items/scheduler.py +184 -0
- solokit/work_items/spec_parser.py +838 -0
- solokit/work_items/spec_validator.py +493 -0
- solokit/work_items/updater.py +157 -0
- solokit/work_items/validator.py +205 -0
- solokit-0.1.1.dist-info/METADATA +640 -0
- solokit-0.1.1.dist-info/RECORD +323 -0
- solokit-0.1.1.dist-info/WHEEL +5 -0
- solokit-0.1.1.dist-info/entry_points.txt +2 -0
- solokit-0.1.1.dist-info/licenses/LICENSE +21 -0
- solokit-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
# PostgreSQL database for production
|
|
5
|
+
db:
|
|
6
|
+
image: postgres:16-alpine
|
|
7
|
+
container_name: fullstack-nextjs-db-prod
|
|
8
|
+
restart: always
|
|
9
|
+
environment:
|
|
10
|
+
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
|
11
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
12
|
+
POSTGRES_DB: ${POSTGRES_DB:-mydb}
|
|
13
|
+
ports:
|
|
14
|
+
- '5432:5432'
|
|
15
|
+
volumes:
|
|
16
|
+
- postgres_data_prod:/var/lib/postgresql/data
|
|
17
|
+
healthcheck:
|
|
18
|
+
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
|
19
|
+
interval: 10s
|
|
20
|
+
timeout: 5s
|
|
21
|
+
retries: 5
|
|
22
|
+
networks:
|
|
23
|
+
- app_network
|
|
24
|
+
|
|
25
|
+
# Next.js application (production)
|
|
26
|
+
app:
|
|
27
|
+
build:
|
|
28
|
+
context: ..
|
|
29
|
+
dockerfile: docker/Dockerfile
|
|
30
|
+
target: runner
|
|
31
|
+
container_name: fullstack-nextjs-app-prod
|
|
32
|
+
restart: always
|
|
33
|
+
depends_on:
|
|
34
|
+
db:
|
|
35
|
+
condition: service_healthy
|
|
36
|
+
environment:
|
|
37
|
+
- NODE_ENV=production
|
|
38
|
+
- DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-mydb}?schema=public
|
|
39
|
+
- SENTRY_DSN=${SENTRY_DSN}
|
|
40
|
+
ports:
|
|
41
|
+
- '3000:3000'
|
|
42
|
+
networks:
|
|
43
|
+
- app_network
|
|
44
|
+
healthcheck:
|
|
45
|
+
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:3000/api/health']
|
|
46
|
+
interval: 30s
|
|
47
|
+
timeout: 10s
|
|
48
|
+
retries: 3
|
|
49
|
+
start_period: 40s
|
|
50
|
+
|
|
51
|
+
volumes:
|
|
52
|
+
postgres_data_prod:
|
|
53
|
+
driver: local
|
|
54
|
+
|
|
55
|
+
networks:
|
|
56
|
+
app_network:
|
|
57
|
+
driver: bridge
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
# PostgreSQL database for development
|
|
5
|
+
db:
|
|
6
|
+
image: postgres:16-alpine
|
|
7
|
+
container_name: fullstack-nextjs-db
|
|
8
|
+
restart: unless-stopped
|
|
9
|
+
environment:
|
|
10
|
+
POSTGRES_USER: postgres
|
|
11
|
+
POSTGRES_PASSWORD: postgres
|
|
12
|
+
POSTGRES_DB: mydb
|
|
13
|
+
ports:
|
|
14
|
+
- '5432:5432'
|
|
15
|
+
volumes:
|
|
16
|
+
- postgres_data:/var/lib/postgresql/data
|
|
17
|
+
healthcheck:
|
|
18
|
+
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
|
19
|
+
interval: 10s
|
|
20
|
+
timeout: 5s
|
|
21
|
+
retries: 5
|
|
22
|
+
|
|
23
|
+
# Next.js application (development)
|
|
24
|
+
app:
|
|
25
|
+
build:
|
|
26
|
+
context: ..
|
|
27
|
+
dockerfile: docker/Dockerfile
|
|
28
|
+
target: builder
|
|
29
|
+
container_name: fullstack-nextjs-app
|
|
30
|
+
restart: unless-stopped
|
|
31
|
+
depends_on:
|
|
32
|
+
db:
|
|
33
|
+
condition: service_healthy
|
|
34
|
+
environment:
|
|
35
|
+
- NODE_ENV=development
|
|
36
|
+
- DATABASE_URL=postgresql://postgres:postgres@db:5432/mydb?schema=public
|
|
37
|
+
ports:
|
|
38
|
+
- '3000:3000'
|
|
39
|
+
volumes:
|
|
40
|
+
- ..:/app
|
|
41
|
+
- /app/node_modules
|
|
42
|
+
- /app/.next
|
|
43
|
+
command: npm run dev
|
|
44
|
+
|
|
45
|
+
volumes:
|
|
46
|
+
postgres_data:
|
|
47
|
+
driver: local
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Config } from 'jest'
|
|
2
|
+
import nextJest from 'next/jest.js'
|
|
3
|
+
|
|
4
|
+
const createJestConfig = nextJest({
|
|
5
|
+
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
|
6
|
+
dir: './',
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
// Add any custom config to be passed to Jest
|
|
10
|
+
const config: Config = {
|
|
11
|
+
coverageProvider: 'v8',
|
|
12
|
+
testEnvironment: 'jsdom',
|
|
13
|
+
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
|
|
17
|
+
export default createJestConfig(config)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{project_name}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "{project_description}",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "next dev",
|
|
9
|
+
"build": "next build",
|
|
10
|
+
"start": "next start",
|
|
11
|
+
"lint": "next lint",
|
|
12
|
+
"type-check": "tsc --noEmit",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
|
|
17
|
+
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,md}\""
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"next": "16.0.1",
|
|
21
|
+
"react": "19.2.0",
|
|
22
|
+
"react-dom": "19.2.0",
|
|
23
|
+
"prisma": "6.19.0",
|
|
24
|
+
"@prisma/client": "6.19.0",
|
|
25
|
+
"zod": "4.1.12",
|
|
26
|
+
"tailwindcss": "4.1.17",
|
|
27
|
+
"@tailwindcss/postcss": "4.1.17",
|
|
28
|
+
"clsx": "2.1.1",
|
|
29
|
+
"tailwind-merge": "3.3.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "5.9.3",
|
|
33
|
+
"@types/node": "20.19.24",
|
|
34
|
+
"@types/react": "19.2.2",
|
|
35
|
+
"@types/react-dom": "19.2.2",
|
|
36
|
+
"eslint": "9.39.1",
|
|
37
|
+
"eslint-config-next": "16.0.1",
|
|
38
|
+
"@typescript-eslint/parser": "8.46.3",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "8.46.3",
|
|
40
|
+
"prettier": "3.6.2",
|
|
41
|
+
"jest": "30.2.0",
|
|
42
|
+
"@types/jest": "30.0.0",
|
|
43
|
+
"ts-jest": "29.4.5",
|
|
44
|
+
"jest-environment-jsdom": "30.2.0",
|
|
45
|
+
"@testing-library/react": "16.3.0",
|
|
46
|
+
"@testing-library/jest-dom": "6.9.1"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect, jest, beforeEach } from "@jest/globals";
|
|
2
|
+
import { NextRequest } from "next/server";
|
|
3
|
+
import { GET, POST } from "@/app/api/example/route";
|
|
4
|
+
|
|
5
|
+
// Mock Prisma client
|
|
6
|
+
jest.mock("@/lib/prisma", () => ({
|
|
7
|
+
prisma: {
|
|
8
|
+
user: {
|
|
9
|
+
findMany: jest.fn(),
|
|
10
|
+
create: jest.fn(),
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// Import the mocked prisma after mocking
|
|
16
|
+
import { prisma } from "@/lib/prisma";
|
|
17
|
+
|
|
18
|
+
describe("API Route: /api/example", () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("GET", () => {
|
|
24
|
+
it("should return a greeting message and users", async () => {
|
|
25
|
+
const mockUsers = [
|
|
26
|
+
{ id: 1, name: "John Doe", email: "john@example.com", createdAt: new Date(), updatedAt: new Date() },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
(prisma.user.findMany as jest.Mock).mockResolvedValue(mockUsers);
|
|
30
|
+
|
|
31
|
+
const response = await GET();
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
|
|
34
|
+
expect(response.status).toBe(200);
|
|
35
|
+
expect(data).toHaveProperty("message");
|
|
36
|
+
expect(data).toHaveProperty("users");
|
|
37
|
+
expect(data.users).toEqual(mockUsers);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should handle database errors", async () => {
|
|
41
|
+
(prisma.user.findMany as jest.Mock).mockRejectedValue(new Error("Database error"));
|
|
42
|
+
|
|
43
|
+
const response = await GET();
|
|
44
|
+
const data = await response.json();
|
|
45
|
+
|
|
46
|
+
expect(response.status).toBe(500);
|
|
47
|
+
expect(data).toHaveProperty("error");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("POST", () => {
|
|
52
|
+
it("should create a new user with valid data", async () => {
|
|
53
|
+
const mockUser = {
|
|
54
|
+
id: 1,
|
|
55
|
+
name: "Jane Doe",
|
|
56
|
+
email: "jane@example.com",
|
|
57
|
+
createdAt: new Date(),
|
|
58
|
+
updatedAt: new Date(),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
(prisma.user.create as jest.Mock).mockResolvedValue(mockUser);
|
|
62
|
+
|
|
63
|
+
const request = new NextRequest("http://localhost:3000/api/example", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
body: JSON.stringify({ name: "Jane Doe", email: "jane@example.com" }),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const response = await POST(request);
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
|
|
71
|
+
expect(response.status).toBe(201);
|
|
72
|
+
expect(data).toEqual(mockUser);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should return validation error for invalid data", async () => {
|
|
76
|
+
const request = new NextRequest("http://localhost:3000/api/example", {
|
|
77
|
+
method: "POST",
|
|
78
|
+
body: JSON.stringify({ name: "", email: "invalid-email" }),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const response = await POST(request);
|
|
82
|
+
const data = await response.json();
|
|
83
|
+
|
|
84
|
+
expect(response.status).toBe(400);
|
|
85
|
+
expect(data).toHaveProperty("error", "Validation failed");
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Add custom jest matchers from jest-dom
|
|
2
|
+
import "@testing-library/jest-dom";
|
|
3
|
+
|
|
4
|
+
// Mock Next.js router
|
|
5
|
+
jest.mock("next/navigation", () => ({
|
|
6
|
+
useRouter() {
|
|
7
|
+
return {
|
|
8
|
+
push: jest.fn(),
|
|
9
|
+
replace: jest.fn(),
|
|
10
|
+
prefetch: jest.fn(),
|
|
11
|
+
};
|
|
12
|
+
},
|
|
13
|
+
useSearchParams() {
|
|
14
|
+
return new URLSearchParams();
|
|
15
|
+
},
|
|
16
|
+
usePathname() {
|
|
17
|
+
return "";
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
// Mock environment variables
|
|
22
|
+
process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, it, expect } from "@jest/globals";
|
|
3
|
+
import ExampleComponent from "@/components/example-component";
|
|
4
|
+
|
|
5
|
+
describe("ExampleComponent", () => {
|
|
6
|
+
it("should render the component with initial count", () => {
|
|
7
|
+
render(<ExampleComponent />);
|
|
8
|
+
expect(screen.getByText(/Count:/i)).toBeInTheDocument();
|
|
9
|
+
expect(screen.getByText(/0/)).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("should have an increment button", () => {
|
|
13
|
+
render(<ExampleComponent />);
|
|
14
|
+
const button = screen.getByRole("button", { name: /increment/i });
|
|
15
|
+
expect(button).toBeInTheDocument();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render the title", () => {
|
|
19
|
+
render(<ExampleComponent />);
|
|
20
|
+
expect(screen.getByText(/Client Component Example/i)).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{project_name}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "{project_description}",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "next dev",
|
|
9
|
+
"build": "next build",
|
|
10
|
+
"start": "next start",
|
|
11
|
+
"lint": "next lint",
|
|
12
|
+
"type-check": "tsc --noEmit",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
|
|
17
|
+
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,md}\"",
|
|
18
|
+
"prepare": "husky install",
|
|
19
|
+
"audit": "npm audit --audit-level=moderate"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"next": "16.0.1",
|
|
23
|
+
"react": "19.2.0",
|
|
24
|
+
"react-dom": "19.2.0",
|
|
25
|
+
"prisma": "6.19.0",
|
|
26
|
+
"@prisma/client": "6.19.0",
|
|
27
|
+
"zod": "4.1.12",
|
|
28
|
+
"tailwindcss": "4.1.17",
|
|
29
|
+
"@tailwindcss/postcss": "4.1.17",
|
|
30
|
+
"clsx": "2.1.1",
|
|
31
|
+
"tailwind-merge": "3.3.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "5.9.3",
|
|
35
|
+
"@types/node": "20.19.24",
|
|
36
|
+
"@types/react": "19.2.2",
|
|
37
|
+
"@types/react-dom": "19.2.2",
|
|
38
|
+
"eslint": "9.39.1",
|
|
39
|
+
"eslint-config-next": "16.0.1",
|
|
40
|
+
"@typescript-eslint/parser": "8.46.3",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "8.46.3",
|
|
42
|
+
"prettier": "3.6.2",
|
|
43
|
+
"jest": "30.2.0",
|
|
44
|
+
"@types/jest": "30.0.0",
|
|
45
|
+
"ts-jest": "29.4.5",
|
|
46
|
+
"jest-environment-jsdom": "30.2.0",
|
|
47
|
+
"@testing-library/react": "16.3.0",
|
|
48
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
49
|
+
"husky": "9.1.7",
|
|
50
|
+
"lint-staged": "16.2.6"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { dirname } from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const compat = new FlatCompat({
|
|
9
|
+
baseDirectory: __dirname,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const eslintConfig = [
|
|
13
|
+
...compat.extends("next/core-web-vitals", "next/typescript"),
|
|
14
|
+
{
|
|
15
|
+
plugins: {
|
|
16
|
+
"jest-dom": (await import("eslint-plugin-jest-dom")).default,
|
|
17
|
+
"testing-library": (await import("eslint-plugin-testing-library")).default,
|
|
18
|
+
},
|
|
19
|
+
rules: {
|
|
20
|
+
"@typescript-eslint/no-unused-vars": [
|
|
21
|
+
"error",
|
|
22
|
+
{ argsIgnorePattern: "^_" },
|
|
23
|
+
],
|
|
24
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
25
|
+
// jest-dom rules
|
|
26
|
+
"jest-dom/prefer-checked": "error",
|
|
27
|
+
"jest-dom/prefer-enabled-disabled": "error",
|
|
28
|
+
"jest-dom/prefer-required": "error",
|
|
29
|
+
"jest-dom/prefer-to-have-attribute": "error",
|
|
30
|
+
// testing-library rules
|
|
31
|
+
"testing-library/await-async-queries": "error",
|
|
32
|
+
"testing-library/no-await-sync-queries": "error",
|
|
33
|
+
"testing-library/no-debugging-utils": "warn",
|
|
34
|
+
"testing-library/prefer-screen-queries": "error",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{project_name}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "{project_description}",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "next dev",
|
|
9
|
+
"build": "next build",
|
|
10
|
+
"start": "next start",
|
|
11
|
+
"lint": "next lint",
|
|
12
|
+
"type-check": "tsc --noEmit",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"test:e2e": "playwright test",
|
|
17
|
+
"test:e2e:ui": "playwright test --ui",
|
|
18
|
+
"test:integration": "jest --testPathPattern=integration",
|
|
19
|
+
"test:mutation": "stryker run",
|
|
20
|
+
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
|
|
21
|
+
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,md}\"",
|
|
22
|
+
"prepare": "husky install",
|
|
23
|
+
"audit": "npm audit --audit-level=moderate",
|
|
24
|
+
"check:duplication": "jscpd .",
|
|
25
|
+
"check:types": "type-coverage"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"next": "16.0.1",
|
|
29
|
+
"react": "19.2.0",
|
|
30
|
+
"react-dom": "19.2.0",
|
|
31
|
+
"prisma": "6.19.0",
|
|
32
|
+
"@prisma/client": "6.19.0",
|
|
33
|
+
"zod": "4.1.12",
|
|
34
|
+
"tailwindcss": "4.1.17",
|
|
35
|
+
"@tailwindcss/postcss": "4.1.17",
|
|
36
|
+
"clsx": "2.1.1",
|
|
37
|
+
"tailwind-merge": "3.3.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"typescript": "5.9.3",
|
|
41
|
+
"@types/node": "20.19.24",
|
|
42
|
+
"@types/react": "19.2.2",
|
|
43
|
+
"@types/react-dom": "19.2.2",
|
|
44
|
+
"eslint": "9.39.1",
|
|
45
|
+
"eslint-config-next": "16.0.1",
|
|
46
|
+
"@typescript-eslint/parser": "8.46.3",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "8.46.3",
|
|
48
|
+
"prettier": "3.6.2",
|
|
49
|
+
"jest": "30.2.0",
|
|
50
|
+
"@types/jest": "30.0.0",
|
|
51
|
+
"ts-jest": "29.4.5",
|
|
52
|
+
"jest-environment-jsdom": "30.2.0",
|
|
53
|
+
"@testing-library/react": "16.3.0",
|
|
54
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
55
|
+
"husky": "9.1.7",
|
|
56
|
+
"lint-staged": "16.2.6",
|
|
57
|
+
"jscpd": "4.0.5",
|
|
58
|
+
"ts-prune": "0.10.3",
|
|
59
|
+
"type-coverage": "2.29.7",
|
|
60
|
+
"@stryker-mutator/core": "9.3.0",
|
|
61
|
+
"@stryker-mutator/jest-runner": "9.3.0",
|
|
62
|
+
"@playwright/test": "1.56.1",
|
|
63
|
+
"playwright": "1.56.1",
|
|
64
|
+
"@axe-core/playwright": "4.11.0",
|
|
65
|
+
"eslint-plugin-jest-dom": "5.5.0",
|
|
66
|
+
"eslint-plugin-testing-library": "7.13.3"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read environment variables from file.
|
|
5
|
+
* https://github.com/motdotla/dotenv
|
|
6
|
+
*/
|
|
7
|
+
// require('dotenv').config();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* See https://playwright.dev/docs/test-configuration.
|
|
11
|
+
*/
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
testDir: './tests/e2e',
|
|
14
|
+
/* Run tests in files in parallel */
|
|
15
|
+
fullyParallel: true,
|
|
16
|
+
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
17
|
+
forbidOnly: !!process.env.CI,
|
|
18
|
+
/* Retry on CI only */
|
|
19
|
+
retries: process.env.CI ? 2 : 0,
|
|
20
|
+
/* Opt out of parallel tests on CI. */
|
|
21
|
+
workers: process.env.CI ? 1 : undefined,
|
|
22
|
+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
23
|
+
reporter: 'html',
|
|
24
|
+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
|
25
|
+
use: {
|
|
26
|
+
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
27
|
+
baseURL: 'http://localhost:3000',
|
|
28
|
+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
|
29
|
+
trace: 'on-first-retry',
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/* Configure projects for major browsers */
|
|
33
|
+
projects: [
|
|
34
|
+
{
|
|
35
|
+
name: 'chromium',
|
|
36
|
+
use: { ...devices['Desktop Chrome'] },
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
{
|
|
40
|
+
name: 'firefox',
|
|
41
|
+
use: { ...devices['Desktop Firefox'] },
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
name: 'webkit',
|
|
46
|
+
use: { ...devices['Desktop Safari'] },
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/* Test against mobile viewports. */
|
|
50
|
+
{
|
|
51
|
+
name: 'Mobile Chrome',
|
|
52
|
+
use: { ...devices['Pixel 5'] },
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Mobile Safari',
|
|
56
|
+
use: { ...devices['iPhone 12'] },
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
|
|
60
|
+
/* Run your local dev server before starting the tests */
|
|
61
|
+
webServer: {
|
|
62
|
+
command: 'npm run dev',
|
|
63
|
+
url: 'http://localhost:3000',
|
|
64
|
+
reuseExistingServer: !process.env.CI,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
|
|
3
|
+
"packageManager": "npm",
|
|
4
|
+
"reporters": ["html", "clear-text", "progress"],
|
|
5
|
+
"testRunner": "jest",
|
|
6
|
+
"jest": {
|
|
7
|
+
"projectType": "custom",
|
|
8
|
+
"configFile": "jest.config.ts",
|
|
9
|
+
"enableFindRelatedTests": true
|
|
10
|
+
},
|
|
11
|
+
"coverageAnalysis": "perTest",
|
|
12
|
+
"mutate": [
|
|
13
|
+
"app/**/*.ts",
|
|
14
|
+
"app/**/*.tsx",
|
|
15
|
+
"lib/**/*.ts",
|
|
16
|
+
"!**/*.test.ts",
|
|
17
|
+
"!**/*.test.tsx",
|
|
18
|
+
"!**/*.spec.ts",
|
|
19
|
+
"!**/*.spec.tsx"
|
|
20
|
+
],
|
|
21
|
+
"thresholds": {
|
|
22
|
+
"high": 80,
|
|
23
|
+
"low": 60,
|
|
24
|
+
"break": 50
|
|
25
|
+
},
|
|
26
|
+
"ignorePatterns": [
|
|
27
|
+
"node_modules",
|
|
28
|
+
".next",
|
|
29
|
+
"coverage",
|
|
30
|
+
"dist",
|
|
31
|
+
"build"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
import { injectAxe, checkA11y } from 'axe-playwright';
|
|
3
|
+
|
|
4
|
+
test.describe('Home Page', () => {
|
|
5
|
+
test('should load the home page', async ({ page }) => {
|
|
6
|
+
await page.goto('/');
|
|
7
|
+
|
|
8
|
+
// Check for the main heading
|
|
9
|
+
await expect(page.getByRole('heading', { name: /full-stack.*next\.js/i })).toBeVisible();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('should display example component', async ({ page }) => {
|
|
13
|
+
await page.goto('/');
|
|
14
|
+
|
|
15
|
+
// Wait for the example component to load
|
|
16
|
+
await expect(page.getByText(/client component example/i)).toBeVisible();
|
|
17
|
+
await expect(page.getByText(/count:/i)).toBeVisible();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should have no accessibility violations', async ({ page }) => {
|
|
21
|
+
await page.goto('/');
|
|
22
|
+
|
|
23
|
+
// Inject axe-core
|
|
24
|
+
await injectAxe(page);
|
|
25
|
+
|
|
26
|
+
// Check for accessibility violations
|
|
27
|
+
await checkA11y(page, undefined, {
|
|
28
|
+
detailedReport: true,
|
|
29
|
+
detailedReportOptions: {
|
|
30
|
+
html: true,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('should navigate and display cards', async ({ page }) => {
|
|
36
|
+
await page.goto('/');
|
|
37
|
+
|
|
38
|
+
// Check that both cards are visible
|
|
39
|
+
await expect(page.getByText(/api routes/i)).toBeVisible();
|
|
40
|
+
await expect(page.getByText(/database/i)).toBeVisible();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should have working increment button', async ({ page }) => {
|
|
44
|
+
await page.goto('/');
|
|
45
|
+
|
|
46
|
+
// Find and click the increment button
|
|
47
|
+
const button = page.getByRole('button', { name: /increment/i });
|
|
48
|
+
await expect(button).toBeVisible();
|
|
49
|
+
|
|
50
|
+
// Initial count should be 0
|
|
51
|
+
await expect(page.getByText(/count: 0/i)).toBeVisible();
|
|
52
|
+
|
|
53
|
+
// Click the button
|
|
54
|
+
await button.click();
|
|
55
|
+
|
|
56
|
+
// Count should now be 1
|
|
57
|
+
await expect(page.getByText(/count: 1/i)).toBeVisible();
|
|
58
|
+
});
|
|
59
|
+
});
|