@etus/bhono-app 0.1.1
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/dist/cli.d.ts +13 -0
- package/dist/cli.js +46 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +26 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/generator.d.ts +14 -0
- package/dist/generator.js +142 -0
- package/dist/generator.js.map +1 -0
- package/dist/generator.test.d.ts +1 -0
- package/dist/generator.test.js +127 -0
- package/dist/generator.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +25 -0
- package/dist/prompts.js +83 -0
- package/dist/prompts.js.map +1 -0
- package/dist/prompts.test.d.ts +1 -0
- package/dist/prompts.test.js +24 -0
- package/dist/prompts.test.js.map +1 -0
- package/dist/providers/cloudflare.d.ts +37 -0
- package/dist/providers/cloudflare.js +61 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/cloudflare.test.d.ts +1 -0
- package/dist/providers/cloudflare.test.js +29 -0
- package/dist/providers/cloudflare.test.js.map +1 -0
- package/dist/providers/github.d.ts +16 -0
- package/dist/providers/github.js +57 -0
- package/dist/providers/github.js.map +1 -0
- package/dist/providers/github.test.d.ts +1 -0
- package/dist/providers/github.test.js +16 -0
- package/dist/providers/github.test.js.map +1 -0
- package/dist/templates.d.ts +8 -0
- package/dist/templates.js +88 -0
- package/dist/templates.js.map +1 -0
- package/dist/templates.test.d.ts +1 -0
- package/dist/templates.test.js +26 -0
- package/dist/templates.test.js.map +1 -0
- package/package.json +36 -0
- package/templates/base/.claude/agents/architect-review.md +160 -0
- package/templates/base/.claude/agents/backend-architect.md +308 -0
- package/templates/base/.claude/agents/code-reviewer.md +170 -0
- package/templates/base/.claude/agents/performance-engineer.md +166 -0
- package/templates/base/.claude/agents/test-automator.md +219 -0
- package/templates/base/.claude/commands/check-skill-rules.md +53 -0
- package/templates/base/.claude/commands/claude-md.md +250 -0
- package/templates/base/.claude/commands/code-prompt.md +212 -0
- package/templates/base/.claude/commands/explain-code.md +194 -0
- package/templates/base/.claude/commands/init-projec.md +89 -0
- package/templates/base/.claude/commands/linear/README.md +297 -0
- package/templates/base/.claude/commands/linear/create-issue.md +190 -0
- package/templates/base/.claude/commands/linear/implement-issue.md +248 -0
- package/templates/base/.claude/commands/linear/process-triage.md +399 -0
- package/templates/base/.claude/commands/linear/setup.md +180 -0
- package/templates/base/.claude/commands/prime.md +9 -0
- package/templates/base/.claude/commands/review-gap.md +10 -0
- package/templates/base/.claude/commands/setup-aa.md +311 -0
- package/templates/base/.claude/commands/ship.md +262 -0
- package/templates/base/.claude/commands/tools.md +3 -0
- package/templates/base/.claude/docs/claude-progress.txt +107 -0
- package/templates/base/.claude/hooks/package-lock.json +556 -0
- package/templates/base/.claude/hooks/package.json +16 -0
- package/templates/base/.claude/hooks/skill-activation-prompt.sh +7 -0
- package/templates/base/.claude/hooks/skill-activation-prompt.ts +142 -0
- package/templates/base/.claude/hooks/tsconfig.json +19 -0
- package/templates/base/.claude/scripts/check-updates.sh +85 -0
- package/templates/base/.claude/scripts/install_pkgs.sh +66 -0
- package/templates/base/.claude/scripts/setup-project.sh +177 -0
- package/templates/base/.claude/scripts/validate-skill-rules.sh +94 -0
- package/templates/base/.claude/settings.json +113 -0
- package/templates/base/.claude/settings.local.json +11 -0
- package/templates/base/.claude/skills/architecture-analyzer/SKILL.md +531 -0
- package/templates/base/.claude/skills/architecture-analyzer/assets/report-template.md +215 -0
- package/templates/base/.claude/skills/architecture-analyzer/references/c4-templates.md +234 -0
- package/templates/base/.claude/skills/architecture-analyzer/references/confidence-levels.md +203 -0
- package/templates/base/.claude/skills/architecture-analyzer/scripts/analyze_structure.py +266 -0
- package/templates/base/.claude/skills/architecture-analyzer/scripts/analyze_tech_debt.py +776 -0
- package/templates/base/.claude/skills/architecture-analyzer/scripts/extract_apis.py +338 -0
- package/templates/base/.claude/skills/architecture-analyzer/scripts/generate_c4.py +283 -0
- package/templates/base/.claude/skills/architecture-analyzer/scripts/generate_erd.py +935 -0
- package/templates/base/.claude/skills/architecture-analyzer/scripts/map_dependencies.py +555 -0
- package/templates/base/.claude/skills/dev-browser/SKILL.md +318 -0
- package/templates/base/.claude/skills/dev-browser/bun.lock +443 -0
- package/templates/base/.claude/skills/dev-browser/package-lock.json +2927 -0
- package/templates/base/.claude/skills/dev-browser/package.json +27 -0
- package/templates/base/.claude/skills/dev-browser/scripts/start-server.ts +117 -0
- package/templates/base/.claude/skills/dev-browser/server.sh +24 -0
- package/templates/base/.claude/skills/dev-browser/src/client.ts +403 -0
- package/templates/base/.claude/skills/dev-browser/src/index.ts +281 -0
- package/templates/base/.claude/skills/dev-browser/src/snapshot/__tests__/snapshot.test.ts +223 -0
- package/templates/base/.claude/skills/dev-browser/src/snapshot/browser-script.ts +877 -0
- package/templates/base/.claude/skills/dev-browser/src/snapshot/index.ts +14 -0
- package/templates/base/.claude/skills/dev-browser/src/snapshot/inject.ts +13 -0
- package/templates/base/.claude/skills/dev-browser/src/types.ts +27 -0
- package/templates/base/.claude/skills/dev-browser/tsconfig.json +36 -0
- package/templates/base/.claude/skills/dev-browser/vitest.config.ts +12 -0
- package/templates/base/.claude/skills/linear/SKILL.md +440 -0
- package/templates/base/.claude/skills/linear/examples.md +262 -0
- package/templates/base/.claude/skills/linear/lib/client.ts +51 -0
- package/templates/base/.claude/skills/linear/lib/config.ts +106 -0
- package/templates/base/.claude/skills/linear/lib/output.ts +34 -0
- package/templates/base/.claude/skills/linear/package-lock.json +698 -0
- package/templates/base/.claude/skills/linear/package.json +27 -0
- package/templates/base/.claude/skills/linear/reference.md +263 -0
- package/templates/base/.claude/skills/linear/scripts/comments/create.ts +47 -0
- package/templates/base/.claude/skills/linear/scripts/comments/list.ts +47 -0
- package/templates/base/.claude/skills/linear/scripts/issues/archive.ts +30 -0
- package/templates/base/.claude/skills/linear/scripts/issues/create.ts +279 -0
- package/templates/base/.claude/skills/linear/scripts/issues/get.ts +68 -0
- package/templates/base/.claude/skills/linear/scripts/issues/list.ts +67 -0
- package/templates/base/.claude/skills/linear/scripts/issues/update.ts +281 -0
- package/templates/base/.claude/skills/linear/scripts/labels/add-to-issue.ts +63 -0
- package/templates/base/.claude/skills/linear/scripts/labels/create.ts +45 -0
- package/templates/base/.claude/skills/linear/scripts/labels/list.ts +30 -0
- package/templates/base/.claude/skills/linear/scripts/list-teams.ts +52 -0
- package/templates/base/.claude/skills/linear/scripts/setup/setup-credentials.ts +96 -0
- package/templates/base/.claude/skills/linear/scripts/status/list.ts +31 -0
- package/templates/base/.claude/skills/linear/scripts/status/set-by-name.ts +78 -0
- package/templates/base/.claude/skills/linear/scripts/status/update.ts +44 -0
- package/templates/base/.claude/skills/linear/scripts/users/list.ts +59 -0
- package/templates/base/.claude/skills/linear/scripts/users/me.ts +20 -0
- package/templates/base/.claude/skills/linear/templates/README.md +203 -0
- package/templates/base/.claude/skills/linear/templates/api-reference.md +258 -0
- package/templates/base/.claude/skills/linear/templates/bug-report.md +99 -0
- package/templates/base/.claude/skills/linear/templates/feature-request.md +118 -0
- package/templates/base/.claude/skills/linear/templates/security-issue.md +162 -0
- package/templates/base/.claude/skills/linear/templates/sprint-task.md +175 -0
- package/templates/base/.claude/skills/linear/templates/tech-debt.md +137 -0
- package/templates/base/.claude/skills/linear/tsconfig.json +17 -0
- package/templates/base/.claude/skills/linear/workflows/issue-lifecycle.md +317 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/SKILL.md +113 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/assets/global-setup.template.js +97 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/assets/playwright.config.template.js +171 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/assets/test-template.spec.js +163 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/examples/README.md +26 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/examples/ads.email-deeplink.spec.ts +12 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/examples/mobile.realism.spec.ts +16 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/examples/smoke.home.spec.ts +6 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/architecture.md +578 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/best-practices.md +260 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/ci-reporting.md +86 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/debugging.md +629 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/mobile-realism.md +50 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/optimization.md +488 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/patterns.md +513 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/resources.md +44 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/references/visual-a11y.md +66 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/scripts/auth-setup.js +202 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/scripts/performance-analyzer.js +240 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/scripts/trace-url.js +132 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/ci/github-actions.playwright.yml +78 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/fixtures.ts +44 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/global-setup.template.js +97 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/global.setup.ts +35 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/helpers/ad-gpt-observer.ts +80 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/helpers/chromium-mobile-profile.ts +93 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/playwright.config.template.js +171 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/playwright.config.ts +126 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/test-template.spec.js +163 -0
- package/templates/base/.claude/skills/playwright-e2e-testing/templates/tests/email-deeplink.ads.spec.ts +44 -0
- package/templates/base/.claude/skills/skill-rules.json +184 -0
- package/templates/base/.claude/skills/wrangler/SKILL.md +209 -0
- package/templates/base/.claude/skills/wrangler/resources/api.md +494 -0
- package/templates/base/.claude/skills/wrangler/resources/bundling.md +83 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/cert.md +64 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/check.md +66 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/containers.md +157 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/d1.md +843 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/delete.md +27 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/deploy.md +139 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/deployments.md +56 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/dev.md +157 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/dispatch-namespace.md +69 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/docs.md +61 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/how-to-run.md +62 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/hyperdrive.md +425 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/init.md +31 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/kv-bulk.md +265 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/kv-key.md +353 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/kv-namespace.md +265 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/login.md +23 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/logout.md +19 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/mtls-certificate.md +69 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/pages.md +175 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/pipelines.md +76 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/queues.md +132 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/r2-bucket.md +342 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/r2-object.md +267 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/r2-sql.md +65 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/rollback.md +40 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/secret.md +308 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/secrets-store-secret.md +100 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/secrets-store-store.md +60 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/setup.md +67 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/tail.md +37 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/telemetry.md +64 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/triggers.md +39 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/types.md +73 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/vectorize.md +941 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/versions.md +95 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/whoami.md +49 -0
- package/templates/base/.claude/skills/wrangler/resources/commands/workflows.md +117 -0
- package/templates/base/.claude/skills/wrangler/resources/commands.md +138 -0
- package/templates/base/.claude/skills/wrangler/resources/configuration.md +2176 -0
- package/templates/base/.claude/skills/wrangler/resources/custom-builds.md +55 -0
- package/templates/base/.claude/skills/wrangler/resources/deprecations.md +279 -0
- package/templates/base/.claude/skills/wrangler/resources/enviroments.md +416 -0
- package/templates/base/.claude/skills/wrangler/resources/extract_sections.py +119 -0
- package/templates/base/.claude/skills/wrangler/resources/process_content.py +94 -0
- package/templates/base/.claude/skills/wrangler/resources/system-enviroments-variables.md +120 -0
- package/templates/base/.dev.vars.example +15 -0
- package/templates/base/.env.example +29 -0
- package/templates/base/.github/workflows/test.yml +127 -0
- package/templates/base/.nycrc.json +16 -0
- package/templates/base/CLAUDE.md +218 -0
- package/templates/base/README.md +670 -0
- package/templates/base/auth-setup-error.png +0 -0
- package/templates/base/config/drizzle.config.ts +10 -0
- package/templates/base/config/eslint.config.js +364 -0
- package/templates/base/config/wrangler.json +76 -0
- package/templates/base/docs/app_spec.txt +879 -0
- package/templates/base/docs/app_spec_template.md +681 -0
- package/templates/base/docs/architecture/README.md +8 -0
- package/templates/base/docs/architecture/data-requirements.md +109 -0
- package/templates/base/docs/architecture/erd.md +91 -0
- package/templates/base/docs/features/feature_list.json +3128 -0
- package/templates/base/docs/hono-boilerplate-plan.md +1774 -0
- package/templates/base/docs/test-coverage-gap-analysis.md +242 -0
- package/templates/base/docs/testing.md +188 -0
- package/templates/base/index.html +16 -0
- package/templates/base/package.json +115 -0
- package/templates/base/playwright.config.ts +158 -0
- package/templates/base/pnpm-lock.yaml +8175 -0
- package/templates/base/scripts/capture-prod-session.ts +250 -0
- package/templates/base/scripts/generate-openapi.ts +23 -0
- package/templates/base/scripts/init.sh +121 -0
- package/templates/base/src/client/__tests__/button.test.tsx +30 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-dashboard-stats-cards-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-quick-action-cards-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-recent-activity-section-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-first-name-in-welcome-message-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-information-in-sidebar-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-render-dashboard-when-authenticated-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-show-navigation-sidebar-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-unauthenticated-should-redirect-to-login-when-not-authenticated-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/login.test.tsx/Login-Page-should-display-Google-OAuth-login-button-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/login.test.tsx/Login-Page-should-have-a-link-back-to-home-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-content-without-waiting-for-authentication-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-page-at--login-route-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/login.test.tsx/Login-Page-should-show-Terms-of-Service-and-Privacy-Policy-links-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/login.test.tsx/Login-Page-should-trigger-OAuth-flow-when-clicking-login-button-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-display-404-text-on-not-found-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-have-navigation-options-on-404-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-render-404-page-for-unknown-routes-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-account-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-integrations-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-settings-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-team-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-display-navigation-links-on-home-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-have-correct-link-destinations-on-home-page-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-home-page-without-authentication-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-login-page-without-authentication-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-dashboard-to-login-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-settings-to-login-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-team-to-login-1.png +0 -0
- package/templates/base/src/client/__tests__/routes/authenticated-layout.test.tsx +252 -0
- package/templates/base/src/client/__tests__/routes/dashboard.test.tsx +136 -0
- package/templates/base/src/client/__tests__/routes/error-components.test.tsx +186 -0
- package/templates/base/src/client/__tests__/routes/login.test.tsx +112 -0
- package/templates/base/src/client/__tests__/routes/navigation.test.tsx +272 -0
- package/templates/base/src/client/__tests__/routes/root-layout.test.tsx +65 -0
- package/templates/base/src/client/__tests__/setup-browser.ts +15 -0
- package/templates/base/src/client/__tests__/setup.ts +32 -0
- package/templates/base/src/client/__tests__/test-utils.tsx +135 -0
- package/templates/base/src/client/api/.gitkeep +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-can-be-collapsed-by-default-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-expands-when-collapsed-and-expand-button-is-clicked-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-handles-logout-button-click-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-handles-navigation-clicks-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-hides-navigation-labels-when-collapsed-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-highlights-active-route-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-renders-sidebar-navigation-items-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-shows-keyboard-shortcut-hint-when-expanded-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-info-when-authenticated-1.png +0 -0
- package/templates/base/src/client/components/__tests__/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-initials-in-avatar-fallback-1.png +0 -0
- package/templates/base/src/client/components/__tests__/error-boundary.test.tsx +97 -0
- package/templates/base/src/client/components/__tests__/sidebar.test.tsx +281 -0
- package/templates/base/src/client/components/error-boundary.tsx +68 -0
- package/templates/base/src/client/components/icons.tsx +106 -0
- package/templates/base/src/client/components/layout/.gitkeep +0 -0
- package/templates/base/src/client/components/sidebar.tsx +267 -0
- package/templates/base/src/client/components/ui/.gitkeep +0 -0
- package/templates/base/src/client/components/ui/__tests__/avatar.test.tsx +308 -0
- package/templates/base/src/client/components/ui/__tests__/card.test.tsx +214 -0
- package/templates/base/src/client/components/ui/__tests__/dialog.test.tsx +297 -0
- package/templates/base/src/client/components/ui/__tests__/error-fallback.test.tsx +145 -0
- package/templates/base/src/client/components/ui/__tests__/input.test.tsx +98 -0
- package/templates/base/src/client/components/ui/__tests__/loading-skeleton.test.tsx +139 -0
- package/templates/base/src/client/components/ui/__tests__/skeleton.test.tsx +44 -0
- package/templates/base/src/client/components/ui/__tests__/sonner.test.tsx +28 -0
- package/templates/base/src/client/components/ui/__tests__/tabs.test.tsx +233 -0
- package/templates/base/src/client/components/ui/avatar.tsx +101 -0
- package/templates/base/src/client/components/ui/badge.tsx +46 -0
- package/templates/base/src/client/components/ui/button.tsx +72 -0
- package/templates/base/src/client/components/ui/card.tsx +86 -0
- package/templates/base/src/client/components/ui/dialog.tsx +140 -0
- package/templates/base/src/client/components/ui/error-fallback.tsx +179 -0
- package/templates/base/src/client/components/ui/form.tsx +172 -0
- package/templates/base/src/client/components/ui/input.tsx +24 -0
- package/templates/base/src/client/components/ui/label.tsx +22 -0
- package/templates/base/src/client/components/ui/loading-skeleton.tsx +154 -0
- package/templates/base/src/client/components/ui/separator.tsx +33 -0
- package/templates/base/src/client/components/ui/skeleton.tsx +16 -0
- package/templates/base/src/client/components/ui/sonner.tsx +29 -0
- package/templates/base/src/client/components/ui/tabs.tsx +121 -0
- package/templates/base/src/client/hooks/.gitkeep +0 -0
- package/templates/base/src/client/hooks/__tests__/use-auth.test.tsx +306 -0
- package/templates/base/src/client/hooks/__tests__/use-theme.test.tsx +172 -0
- package/templates/base/src/client/hooks/use-auth.ts +53 -0
- package/templates/base/src/client/hooks/use-theme.tsx +78 -0
- package/templates/base/src/client/index.css +881 -0
- package/templates/base/src/client/lib/query-client.ts +11 -0
- package/templates/base/src/client/lib/utils.ts +7 -0
- package/templates/base/src/client/main.tsx +26 -0
- package/templates/base/src/client/routeTree.gen.ts +258 -0
- package/templates/base/src/client/router.ts +15 -0
- package/templates/base/src/client/routes/$.tsx +77 -0
- package/templates/base/src/client/routes/.gitkeep +0 -0
- package/templates/base/src/client/routes/__root.tsx +34 -0
- package/templates/base/src/client/routes/__tests__/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-accept-invitation-button-1.png +0 -0
- package/templates/base/src/client/routes/__tests__/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-decline-button-linking-to-homepage-1.png +0 -0
- package/templates/base/src/client/routes/__tests__/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-invitation-details--email--workspace--role--1.png +0 -0
- package/templates/base/src/client/routes/__tests__/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-have-a-logo-link-to-homepage-1.png +0 -0
- package/templates/base/src/client/routes/__tests__/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-render-invitation-page-with-inviter-name-and-workspace-1.png +0 -0
- package/templates/base/src/client/routes/__tests__/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-show-terms-of-service-and-privacy-policy-links-1.png +0 -0
- package/templates/base/src/client/routes/__tests__/invite.test.tsx +138 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/account.test.tsx/Account-Page-should-render-Active-Sessions-section-with-session-cards-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/account.test.tsx/Account-Page-should-render-page-with-correct-title--Account--1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/account.test.tsx/Account-Page-should-show-API-Access-section-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/account.test.tsx/Account-Page-should-show-Connected-Accounts-section-with-Google-connected-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/account.test.tsx/Account-Page-should-show-Danger-Zone-section-with-delete-button-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/account.test.tsx/Account-Page-should-show-Security-section-with-Two-Factor-Authentication-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-API-documentation-section-should-display-API-documentation-link-section-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-Create-Webhook-Dialog-should-have-Add-Webhook-trigger-button-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-category-filters-should-display-all-category-buttons-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-all-integration-cards-with-names-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-category-badges-on-cards-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Configure-button-for-connected-integrations-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connect-button-for-not-connected-integrations-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connected-badge-for-connected-integrations-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Available-Integrations-section-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Webhooks-section-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-connected-count-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-page-description-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-search-input-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-render-with-correct-title--Integrations--1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-filter-integrations-based-on-search-query-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-search-by-description-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-show-no-results-message-when-search-has-no-matches-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-existing-webhook-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-events-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-last-delivery-info-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-success-status-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-have-Add-Webhook-button-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-connected-accounts-section-with-Google-provider-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-sessions-and-danger-zone-sections-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-all-notification-toggle-options-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-email-notifications-section-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-toggle-switches-for-notification-options-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-have-toggles-checked-by-default-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display--Save-Changes--button-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-personal-information-form-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-profile-picture-section-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-email-in-disabled-email-input-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-initials-in-avatar-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-name-in-the-name-input-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-all-three-tabs-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-page-description-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-render-with-correct-title--Settings--1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-show-Profile-tab-as-default-active-tab-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Account-tab-when-clicked-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Notifications-tab-when-clicked-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/team.test.tsx/Team-Page-should-display-Active-Members-section-with-member-count-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/team.test.tsx/Team-Page-should-display-Pending-Invitations-section-with-invitation-details-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/team.test.tsx/Team-Page-should-have-invite-member-button-that-can-be-clicked-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/team.test.tsx/Team-Page-should-render-page-with-correct-title-and-description-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/team.test.tsx/Team-Page-should-render-search-input-that-filters-team-members-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/__screenshots__/team.test.tsx/Team-Page-should-show-current-user-with---you---indicator-and-role-badge-1.png +0 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/account.test.tsx +324 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/integrations.test.tsx +520 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/settings.test.tsx +414 -0
- package/templates/base/src/client/routes/_authenticated/__tests__/team.test.tsx +374 -0
- package/templates/base/src/client/routes/_authenticated/account.tsx +416 -0
- package/templates/base/src/client/routes/_authenticated/dashboard.tsx +151 -0
- package/templates/base/src/client/routes/_authenticated/integrations.tsx +553 -0
- package/templates/base/src/client/routes/_authenticated/settings.tsx +310 -0
- package/templates/base/src/client/routes/_authenticated/team.tsx +395 -0
- package/templates/base/src/client/routes/_authenticated.tsx +69 -0
- package/templates/base/src/client/routes/index.tsx +155 -0
- package/templates/base/src/client/routes/invite.$token.tsx +191 -0
- package/templates/base/src/client/routes/login.tsx +92 -0
- package/templates/base/src/server/__tests__/fixtures.ts +461 -0
- package/templates/base/src/server/__tests__/mocks/__tests__/db.test.ts +239 -0
- package/templates/base/src/server/__tests__/mocks/__tests__/kv.test.ts +293 -0
- package/templates/base/src/server/__tests__/mocks/__tests__/r2.test.ts +363 -0
- package/templates/base/src/server/__tests__/mocks/db.ts +186 -0
- package/templates/base/src/server/__tests__/mocks/index.ts +33 -0
- package/templates/base/src/server/__tests__/mocks/kv.ts +286 -0
- package/templates/base/src/server/__tests__/mocks/r2.ts +397 -0
- package/templates/base/src/server/__tests__/setup.ts +281 -0
- package/templates/base/src/server/auth/__tests__/guards.test.ts +162 -0
- package/templates/base/src/server/auth/guards.ts +92 -0
- package/templates/base/src/server/auth/permissions.test.ts +45 -0
- package/templates/base/src/server/auth/permissions.ts +139 -0
- package/templates/base/src/server/auth/roles.test.ts +169 -0
- package/templates/base/src/server/auth/roles.ts +141 -0
- package/templates/base/src/server/db/client.ts +12 -0
- package/templates/base/src/server/db/schema/accounts.ts +20 -0
- package/templates/base/src/server/db/schema/audit-logs.ts +26 -0
- package/templates/base/src/server/db/schema/index.ts +7 -0
- package/templates/base/src/server/db/schema/invitations.ts +30 -0
- package/templates/base/src/server/db/schema/refresh-tokens.ts +22 -0
- package/templates/base/src/server/db/schema/user-accounts.ts +25 -0
- package/templates/base/src/server/db/schema/users.ts +33 -0
- package/templates/base/src/server/db/seed.ts +267 -0
- package/templates/base/src/server/env.test.ts +84 -0
- package/templates/base/src/server/env.ts +78 -0
- package/templates/base/src/server/index.ts +82 -0
- package/templates/base/src/server/lib/audit.ts +73 -0
- package/templates/base/src/server/lib/audited-db.test.ts +107 -0
- package/templates/base/src/server/lib/audited-db.ts +154 -0
- package/templates/base/src/server/lib/email.test.ts +116 -0
- package/templates/base/src/server/lib/email.ts +82 -0
- package/templates/base/src/server/lib/errors.test.ts +49 -0
- package/templates/base/src/server/lib/errors.ts +64 -0
- package/templates/base/src/server/lib/oauth.test.ts +238 -0
- package/templates/base/src/server/lib/oauth.ts +113 -0
- package/templates/base/src/server/lib/pagination.test.ts +52 -0
- package/templates/base/src/server/lib/pagination.ts +32 -0
- package/templates/base/src/server/lib/password.test.ts +151 -0
- package/templates/base/src/server/lib/password.ts +151 -0
- package/templates/base/src/server/lib/providers.test.ts +105 -0
- package/templates/base/src/server/lib/providers.ts +62 -0
- package/templates/base/src/server/lib/r2-storage.test.ts +202 -0
- package/templates/base/src/server/lib/r2-storage.ts +107 -0
- package/templates/base/src/server/lib/schema-helpers.ts +16 -0
- package/templates/base/src/server/lib/session.test.ts +758 -0
- package/templates/base/src/server/lib/session.ts +267 -0
- package/templates/base/src/server/lib/tokens.test.ts +208 -0
- package/templates/base/src/server/lib/tokens.ts +65 -0
- package/templates/base/src/server/lib/transaction.test.ts +45 -0
- package/templates/base/src/server/lib/transaction.ts +24 -0
- package/templates/base/src/server/middleware/account.test.ts +201 -0
- package/templates/base/src/server/middleware/account.ts +66 -0
- package/templates/base/src/server/middleware/auth.test.ts +345 -0
- package/templates/base/src/server/middleware/auth.ts +146 -0
- package/templates/base/src/server/middleware/cors.test.ts +88 -0
- package/templates/base/src/server/middleware/cors.ts +26 -0
- package/templates/base/src/server/middleware/error-handler.test.ts +69 -0
- package/templates/base/src/server/middleware/error-handler.ts +43 -0
- package/templates/base/src/server/middleware/index.ts +8 -0
- package/templates/base/src/server/middleware/rate-limit.test.ts +472 -0
- package/templates/base/src/server/middleware/rate-limit.ts +294 -0
- package/templates/base/src/server/middleware/request-context.test.ts +175 -0
- package/templates/base/src/server/middleware/request-context.ts +28 -0
- package/templates/base/src/server/middleware/request-logger.test.ts +92 -0
- package/templates/base/src/server/middleware/request-logger.ts +50 -0
- package/templates/base/src/server/routes/accounts/__tests__/handlers.test.ts +460 -0
- package/templates/base/src/server/routes/accounts/handlers.ts +179 -0
- package/templates/base/src/server/routes/accounts/index.ts +55 -0
- package/templates/base/src/server/routes/accounts/routes.ts +205 -0
- package/templates/base/src/server/routes/accounts/schemas.ts +31 -0
- package/templates/base/src/server/routes/api.ts +37 -0
- package/templates/base/src/server/routes/audits/__tests__/handlers.test.ts +349 -0
- package/templates/base/src/server/routes/audits/handlers.ts +37 -0
- package/templates/base/src/server/routes/audits/index.ts +14 -0
- package/templates/base/src/server/routes/audits/routes.ts +29 -0
- package/templates/base/src/server/routes/audits/schemas.ts +43 -0
- package/templates/base/src/server/routes/auth/__tests__/handlers.test.ts +381 -0
- package/templates/base/src/server/routes/auth/handlers.ts +222 -0
- package/templates/base/src/server/routes/auth/index.ts +37 -0
- package/templates/base/src/server/routes/auth/routes.ts +136 -0
- package/templates/base/src/server/routes/auth/schemas.ts +48 -0
- package/templates/base/src/server/routes/auth/test-login.ts +156 -0
- package/templates/base/src/server/routes/health/__tests__/handlers.test.ts +237 -0
- package/templates/base/src/server/routes/health/handlers.ts +83 -0
- package/templates/base/src/server/routes/health/index.ts +13 -0
- package/templates/base/src/server/routes/health/routes.ts +90 -0
- package/templates/base/src/server/routes/index.ts +53 -0
- package/templates/base/src/server/routes/invitations/__tests__/handlers.test.ts +473 -0
- package/templates/base/src/server/routes/invitations/handlers.ts +71 -0
- package/templates/base/src/server/routes/invitations/index.ts +25 -0
- package/templates/base/src/server/routes/invitations/routes.ts +69 -0
- package/templates/base/src/server/routes/invitations/schemas.ts +39 -0
- package/templates/base/src/server/routes/openapi.ts +14 -0
- package/templates/base/src/server/routes/schemas.ts +53 -0
- package/templates/base/src/server/routes/storage/__tests__/handlers.test.ts +408 -0
- package/templates/base/src/server/routes/storage/handlers.ts +100 -0
- package/templates/base/src/server/routes/storage/index.ts +42 -0
- package/templates/base/src/server/routes/storage/routes.ts +91 -0
- package/templates/base/src/server/routes/storage/schemas.ts +56 -0
- package/templates/base/src/server/routes/users/__tests__/handlers.test.ts +526 -0
- package/templates/base/src/server/routes/users/handlers.ts +228 -0
- package/templates/base/src/server/routes/users/index.ts +67 -0
- package/templates/base/src/server/routes/users/routes.ts +265 -0
- package/templates/base/src/server/routes/users/schemas.ts +67 -0
- package/templates/base/src/server/services/__tests__/accounts.test.ts +764 -0
- package/templates/base/src/server/services/__tests__/audits.test.ts +235 -0
- package/templates/base/src/server/services/__tests__/auth.test.ts +765 -0
- package/templates/base/src/server/services/__tests__/invitations.test.ts +704 -0
- package/templates/base/src/server/services/__tests__/users.test.ts +755 -0
- package/templates/base/src/server/services/accounts.ts +269 -0
- package/templates/base/src/server/services/audits.ts +82 -0
- package/templates/base/src/server/services/auth.ts +225 -0
- package/templates/base/src/server/services/index.ts +6 -0
- package/templates/base/src/server/services/invitations.ts +306 -0
- package/templates/base/src/server/services/users.ts +350 -0
- package/templates/base/src/server/types/auth.ts +36 -0
- package/templates/base/src/server/types/index.ts +117 -0
- package/templates/base/src/shared/schemas/.gitkeep +0 -0
- package/templates/base/src/shared/schemas/__tests__/schemas.test.ts +547 -0
- package/templates/base/src/shared/schemas/account.ts +15 -0
- package/templates/base/src/shared/schemas/index.ts +6 -0
- package/templates/base/src/shared/schemas/invitation.ts +9 -0
- package/templates/base/src/shared/schemas/profile.ts +10 -0
- package/templates/base/src/shared/schemas/user.ts +16 -0
- package/templates/base/src/shared/schemas/webhook.ts +12 -0
- package/templates/base/src/shared/types/.gitkeep +0 -0
- package/templates/base/src/shared/types/account.ts +12 -0
- package/templates/base/src/shared/types/api.ts +1399 -0
- package/templates/base/src/shared/types/auth.ts +24 -0
- package/templates/base/src/shared/types/index.ts +5 -0
- package/templates/base/src/shared/types/user.ts +31 -0
- package/templates/base/src/test/vitest-zod-matcher.ts +37 -0
- package/templates/base/src/test/vitest.d.ts +19 -0
- package/templates/base/tests/e2e/README.md +141 -0
- package/templates/base/tests/e2e/a11y/accessibility.spec.ts +925 -0
- package/templates/base/tests/e2e/a11y/keyboard-navigation.spec.ts +610 -0
- package/templates/base/tests/e2e/api/accounts.spec.ts +148 -0
- package/templates/base/tests/e2e/api/audit-logs.spec.ts +130 -0
- package/templates/base/tests/e2e/api/authenticated-api.spec.ts +311 -0
- package/templates/base/tests/e2e/api/storage.spec.ts +109 -0
- package/templates/base/tests/e2e/auth-flows.unauth.spec.ts +117 -0
- package/templates/base/tests/e2e/auth-logout.unauth.spec.ts +103 -0
- package/templates/base/tests/e2e/auth.setup.ts +115 -0
- package/templates/base/tests/e2e/auth.spec.ts +146 -0
- package/templates/base/tests/e2e/compatibility/cross-browser.spec.ts +152 -0
- package/templates/base/tests/e2e/compatibility/cross-browser.spec.ts-snapshots/login-chromium-chromium-darwin.png +0 -0
- package/templates/base/tests/e2e/crud/account.spec.ts +356 -0
- package/templates/base/tests/e2e/crud/integrations.spec.ts +419 -0
- package/templates/base/tests/e2e/crud/team.spec.ts +287 -0
- package/templates/base/tests/e2e/crud/users.spec.ts +239 -0
- package/templates/base/tests/e2e/errors/error-boundary.spec.ts +428 -0
- package/templates/base/tests/e2e/errors/error-handling.spec.ts +47 -0
- package/templates/base/tests/e2e/errors/error-handling.unauth.spec.ts +205 -0
- package/templates/base/tests/e2e/fixtures.ts +266 -0
- package/templates/base/tests/e2e/forms/validation.spec.ts +569 -0
- package/templates/base/tests/e2e/invitations/invite-flow.unauth.spec.ts +204 -0
- package/templates/base/tests/e2e/journeys/account-lifecycle.spec.ts +314 -0
- package/templates/base/tests/e2e/journeys/audit-investigation.spec.ts +299 -0
- package/templates/base/tests/e2e/journeys/auth-onboarding.spec.ts +232 -0
- package/templates/base/tests/e2e/journeys/critical-flows.spec.ts +281 -0
- package/templates/base/tests/e2e/journeys/error-recovery.spec.ts +354 -0
- package/templates/base/tests/e2e/journeys/file-management.spec.ts +307 -0
- package/templates/base/tests/e2e/journeys/integrations.spec.ts +372 -0
- package/templates/base/tests/e2e/journeys/multi-account.spec.ts +317 -0
- package/templates/base/tests/e2e/journeys/rbac-enforcement.spec.ts +389 -0
- package/templates/base/tests/e2e/journeys/settings-profile.spec.ts +400 -0
- package/templates/base/tests/e2e/journeys/team-collaboration.spec.ts +410 -0
- package/templates/base/tests/e2e/mobile/responsive.spec.ts +178 -0
- package/templates/base/tests/e2e/navigation/routing.spec.ts +371 -0
- package/templates/base/tests/e2e/navigation/sidebar.spec.ts +425 -0
- package/templates/base/tests/e2e/pages/ui-features.spec.ts +393 -0
- package/templates/base/tests/e2e/performance/baselines.spec.ts +162 -0
- package/templates/base/tests/e2e/performance/benchmarks.spec.ts +371 -0
- package/templates/base/tests/e2e/smoke.unauth.spec.ts +196 -0
- package/templates/base/tests/e2e/visual/components.spec.ts +650 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/confirmation-dialog-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/dark-mode-background-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/dark-mode-card-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/dark-mode-sidebar-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/dashboard-card-single-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/dialog-content-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/dialog-with-backdrop-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/empty-search-results-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/input-default-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/input-focus-ring-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/primary-button-focus-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/primary-button-hover-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/primary-button-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/sidebar-active-nav-item-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/sidebar-collapsed-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/components.spec.ts-snapshots/sidebar-navigation-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts +192 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/account-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/create-webhook-dialog-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/dashboard-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/delete-account-dialog-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/integrations-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/invite-member-dialog-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/login-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/settings-account-tab-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/settings-profile-tab-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/sidebar-navigation-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/core-components.spec.ts-snapshots/team-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts +230 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/404-page-chromium-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/404-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/account-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/login-page-chromium-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/login-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/settings-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/team-invite-dialog-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/team-page-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/screenshots.spec.ts-snapshots/webhook-create-dialog-visual-darwin.png +0 -0
- package/templates/base/tests/e2e/visual/theme-colors.spec.ts +293 -0
- package/templates/base/tests/e2e/visual/ui-components.spec.ts +502 -0
- package/templates/base/tests/integration/accounts/crud.test.ts +1402 -0
- package/templates/base/tests/integration/audits/list.test.ts +1133 -0
- package/templates/base/tests/integration/auth/auth-service.test.ts +415 -0
- package/templates/base/tests/integration/auth/invitation-token.test.ts +529 -0
- package/templates/base/tests/integration/auth/logout.test.ts +524 -0
- package/templates/base/tests/integration/auth/oauth.test.ts +768 -0
- package/templates/base/tests/integration/auth/refresh-token.test.ts +364 -0
- package/templates/base/tests/integration/auth/session-expiry.test.ts +569 -0
- package/templates/base/tests/integration/auth/session.test.ts +520 -0
- package/templates/base/tests/integration/auth/super-admin.test.ts +451 -0
- package/templates/base/tests/integration/authorization/analytics-role.test.ts +1026 -0
- package/templates/base/tests/integration/authorization/billing-role.test.ts +776 -0
- package/templates/base/tests/integration/authorization/guards-roles.test.ts +539 -0
- package/templates/base/tests/integration/authorization/multi-tenancy.test.ts +1112 -0
- package/templates/base/tests/integration/authorization/role-hierarchy.test.ts +931 -0
- package/templates/base/tests/integration/authorization/roles.test.ts +1347 -0
- package/templates/base/tests/integration/config/production-behavior.test.ts +536 -0
- package/templates/base/tests/integration/db/constraints.test.ts +695 -0
- package/templates/base/tests/integration/fixtures/accounts.ts +136 -0
- package/templates/base/tests/integration/fixtures/index.ts +232 -0
- package/templates/base/tests/integration/fixtures/invitations.ts +195 -0
- package/templates/base/tests/integration/fixtures/users.ts +144 -0
- package/templates/base/tests/integration/health/health.test.ts +351 -0
- package/templates/base/tests/integration/invitations/crud.test.ts +1457 -0
- package/templates/base/tests/integration/invitations/email.test.ts +506 -0
- package/templates/base/tests/integration/lib/email.test.ts +174 -0
- package/templates/base/tests/integration/lib/oauth.test.ts +368 -0
- package/templates/base/tests/integration/lib/password.test.ts +192 -0
- package/templates/base/tests/integration/lib/schema-helpers.test.ts +129 -0
- package/templates/base/tests/integration/lib/tokens.test.ts +304 -0
- package/templates/base/tests/integration/middleware/auth.test.ts +499 -0
- package/templates/base/tests/integration/middleware/cors.test.ts +334 -0
- package/templates/base/tests/integration/middleware/request-context.test.ts +156 -0
- package/templates/base/tests/integration/middleware/request-logger.test.ts +313 -0
- package/templates/base/tests/integration/performance/response-times.test.ts +509 -0
- package/templates/base/tests/integration/security/cookie-security.test.ts +567 -0
- package/templates/base/tests/integration/security/csrf-protection.test.ts +542 -0
- package/templates/base/tests/integration/security/jwt-validation.test.ts +209 -0
- package/templates/base/tests/integration/security/log-sanitization.test.ts +658 -0
- package/templates/base/tests/integration/security/rate-limiting.test.ts +1251 -0
- package/templates/base/tests/integration/security/sql-injection.test.ts +663 -0
- package/templates/base/tests/integration/security/token-hashing.test.ts +371 -0
- package/templates/base/tests/integration/security/xss-prevention.test.ts +541 -0
- package/templates/base/tests/integration/setup.ts +834 -0
- package/templates/base/tests/integration/smoke.test.ts +288 -0
- package/templates/base/tests/integration/storage/upload.test.ts +1162 -0
- package/templates/base/tests/integration/storage/validation.test.ts +746 -0
- package/templates/base/tests/integration/users/crud.test.ts +1297 -0
- package/templates/base/tests/integration/users/list.test.ts +698 -0
- package/templates/base/tests/integration/vitest.config.ts +80 -0
- package/templates/base/tsconfig.app.json +18 -0
- package/templates/base/tsconfig.json +39 -0
- package/templates/base/tsconfig.node.json +16 -0
- package/templates/base/tsconfig.server.json +26 -0
- package/templates/base/tsconfig.tsbuildinfo +1 -0
- package/templates/base/vite.config.ts +46 -0
- package/templates/base/vitest.config.browser.ts +47 -0
- package/templates/base/vitest.config.frontend.ts +47 -0
- package/templates/base/vitest.config.ts +82 -0
- package/templates/base/vitest.workspace.ts +22 -0
- package/templates/modules/audit-logs/.gitkeep +0 -0
- package/templates/modules/billing/.gitkeep +0 -0
- package/templates/modules/invitations/.gitkeep +0 -0
- package/templates/modules/storage/.gitkeep +0 -0
- package/templates/modules/webhooks/.gitkeep +0 -0
- package/templates/providers/auth-email/.gitkeep +0 -0
- package/templates/providers/auth-github/.gitkeep +0 -0
- package/templates/providers/auth-google/.gitkeep +0 -0
- package/templates/providers/email-resend/.gitkeep +0 -0
- package/templates/providers/email-sendgrid/.gitkeep +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { test, expect } from './fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auth Flow Tests (Unauthenticated)
|
|
5
|
+
*
|
|
6
|
+
* These tests verify authentication flows without requiring an authenticated session.
|
|
7
|
+
* Tests are run with the 'chromium-unauth' project (no storageState).
|
|
8
|
+
*
|
|
9
|
+
* @tags @auth
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
test.describe('Auth Flows @auth', () => {
|
|
13
|
+
test.describe('Login Page', () => {
|
|
14
|
+
test('should display login options', async ({ page }) => {
|
|
15
|
+
await page.goto('/login')
|
|
16
|
+
|
|
17
|
+
// Should see welcome message
|
|
18
|
+
await expect(page.getByText('Welcome back')).toBeVisible()
|
|
19
|
+
|
|
20
|
+
// Should have Google OAuth button
|
|
21
|
+
const googleButton = page.getByRole('button', { name: /continue with google/i })
|
|
22
|
+
await expect(googleButton).toBeVisible()
|
|
23
|
+
await expect(googleButton).toBeEnabled()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('Google OAuth button should redirect to OAuth provider', async ({ page }) => {
|
|
27
|
+
await page.goto('/login')
|
|
28
|
+
|
|
29
|
+
// Get the Google OAuth button
|
|
30
|
+
const googleButton = page.getByRole('button', { name: /continue with google/i })
|
|
31
|
+
await expect(googleButton).toBeVisible()
|
|
32
|
+
|
|
33
|
+
// Click and wait for navigation to OAuth provider
|
|
34
|
+
// We expect either:
|
|
35
|
+
// 1. Redirect to Google's OAuth page (accounts.google.com)
|
|
36
|
+
// 2. Redirect to our OAuth endpoint (/auth/login) which then redirects to Google
|
|
37
|
+
const [response] = await Promise.all([
|
|
38
|
+
page.waitForResponse(
|
|
39
|
+
(resp) =>
|
|
40
|
+
resp.url().includes('accounts.google.com') ||
|
|
41
|
+
resp.url().includes('/auth/login') ||
|
|
42
|
+
resp.url().includes('/auth/google'),
|
|
43
|
+
{ timeout: 10000 }
|
|
44
|
+
).catch(() => null),
|
|
45
|
+
googleButton.click(),
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
// Should have navigated away from login page
|
|
49
|
+
// Either to Google OAuth or to our auth endpoint
|
|
50
|
+
await expect
|
|
51
|
+
.poll(
|
|
52
|
+
async () => {
|
|
53
|
+
const url = page.url()
|
|
54
|
+
return (
|
|
55
|
+
url.includes('accounts.google.com') ||
|
|
56
|
+
url.includes('/auth/login') ||
|
|
57
|
+
url.includes('/auth/google') ||
|
|
58
|
+
// If OAuth is configured differently, we at least shouldn't be on login anymore
|
|
59
|
+
!url.includes('/login')
|
|
60
|
+
)
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
timeout: 10000,
|
|
64
|
+
message: 'Expected to navigate away from login page to OAuth provider',
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
.toBeTruthy()
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test.describe('Protected Route Redirects', () => {
|
|
72
|
+
const protectedRoutes = [
|
|
73
|
+
{ path: '/dashboard', name: 'Dashboard' },
|
|
74
|
+
{ path: '/settings', name: 'Settings' },
|
|
75
|
+
{ path: '/team', name: 'Team' },
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
for (const route of protectedRoutes) {
|
|
79
|
+
test(`unauthenticated user should be redirected from ${route.name}`, async ({ page }) => {
|
|
80
|
+
// Try to access protected route without auth
|
|
81
|
+
await page.goto(route.path)
|
|
82
|
+
|
|
83
|
+
// Should be redirected to login page
|
|
84
|
+
await expect(page).toHaveURL(/login/, { timeout: 10000 })
|
|
85
|
+
|
|
86
|
+
// Login page should be visible
|
|
87
|
+
await expect(page.getByText('Welcome back')).toBeVisible()
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test.describe('Auth Endpoints', () => {
|
|
93
|
+
test('/auth/me should return 401 for unauthenticated requests', async ({ request, baseURL }) => {
|
|
94
|
+
const response = await request.get(`${baseURL}/auth/me`, {
|
|
95
|
+
failOnStatusCode: false,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// Should return 401 Unauthorized
|
|
99
|
+
expect(response.status()).toBe(401)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('/auth/logout should handle unauthenticated logout gracefully', async ({
|
|
103
|
+
page,
|
|
104
|
+
request,
|
|
105
|
+
baseURL,
|
|
106
|
+
}) => {
|
|
107
|
+
// Logout endpoint should not crash for unauthenticated users
|
|
108
|
+
const response = await request.post(`${baseURL}/auth/logout`, {
|
|
109
|
+
failOnStatusCode: false,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Should either redirect or return success (implementation dependent)
|
|
113
|
+
// Accept 200, 302 (redirect), or 401
|
|
114
|
+
expect([200, 302, 401]).toContain(response.status())
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
})
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { test, expect, isAuthenticated, waitForNavigation } from './fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Logout Flow Tests
|
|
5
|
+
*
|
|
6
|
+
* These tests verify logout functionality. They are in a separate file (.unauth.spec.ts)
|
|
7
|
+
* to run in the chromium-unauth project, preventing session invalidation from
|
|
8
|
+
* affecting other parallel tests.
|
|
9
|
+
*
|
|
10
|
+
* @tags @auth @logout
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
test.describe('Logout Flow @auth @logout', () => {
|
|
14
|
+
// Run serially - logout tests invalidate session
|
|
15
|
+
test.describe.configure({ mode: 'serial' })
|
|
16
|
+
|
|
17
|
+
test('logout should clear session and redirect to login', async ({ page }) => {
|
|
18
|
+
// First authenticate via test-login
|
|
19
|
+
const loginResponse = await page.request.post('/auth/test-login', {
|
|
20
|
+
data: {
|
|
21
|
+
email: 'logout-test@example.com',
|
|
22
|
+
name: 'Logout Test User',
|
|
23
|
+
},
|
|
24
|
+
failOnStatusCode: false,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test.skip(!loginResponse.ok(), 'Test login endpoint not available')
|
|
28
|
+
|
|
29
|
+
// Navigate to dashboard first
|
|
30
|
+
await page.goto('/dashboard')
|
|
31
|
+
await expect(page).not.toHaveURL(/login/)
|
|
32
|
+
|
|
33
|
+
// Find and click logout button/link
|
|
34
|
+
const logoutButton = page.getByRole('button', { name: /logout|sign out|log out/i })
|
|
35
|
+
const logoutLink = page.getByRole('link', { name: /logout|sign out|log out/i })
|
|
36
|
+
|
|
37
|
+
let logoutElement = null
|
|
38
|
+
|
|
39
|
+
if (await logoutButton.isVisible({ timeout: 3000 }).catch(() => false)) {
|
|
40
|
+
logoutElement = logoutButton
|
|
41
|
+
} else if (await logoutLink.isVisible({ timeout: 3000 }).catch(() => false)) {
|
|
42
|
+
logoutElement = logoutLink
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!logoutElement) {
|
|
46
|
+
// Try opening user menu first
|
|
47
|
+
const userMenu = page.getByRole('button', { name: /account|profile|user|menu/i })
|
|
48
|
+
if (await userMenu.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
49
|
+
await userMenu.click()
|
|
50
|
+
|
|
51
|
+
// Look for logout in dropdown
|
|
52
|
+
const dropdownLogout = page.getByRole('menuitem', { name: /logout|sign out/i })
|
|
53
|
+
if (await dropdownLogout.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
54
|
+
logoutElement = dropdownLogout
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Skip if no logout element found
|
|
60
|
+
test.skip(!logoutElement, 'Logout button/link not found in UI')
|
|
61
|
+
|
|
62
|
+
// Click logout
|
|
63
|
+
await logoutElement!.click()
|
|
64
|
+
|
|
65
|
+
// Should be redirected to login page
|
|
66
|
+
await expect(page).toHaveURL(/login/, { timeout: 10000 })
|
|
67
|
+
|
|
68
|
+
// Verify session is cleared by trying to access protected route
|
|
69
|
+
await page.goto('/dashboard')
|
|
70
|
+
await expect(page).toHaveURL(/login/)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('logout via API should clear session', async ({ page, request, baseURL }) => {
|
|
74
|
+
// First authenticate via test-login
|
|
75
|
+
const loginResponse = await page.request.post('/auth/test-login', {
|
|
76
|
+
data: {
|
|
77
|
+
email: 'api-logout-test@example.com',
|
|
78
|
+
name: 'API Logout Test User',
|
|
79
|
+
},
|
|
80
|
+
failOnStatusCode: false,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test.skip(!loginResponse.ok(), 'Test login endpoint not available')
|
|
84
|
+
|
|
85
|
+
// Verify authenticated
|
|
86
|
+
const authResponse = await request.get(`${baseURL}/auth/me`)
|
|
87
|
+
test.skip(!authResponse.ok(), 'Not authenticated - cannot test logout')
|
|
88
|
+
|
|
89
|
+
// Call logout endpoint
|
|
90
|
+
const logoutResponse = await request.post(`${baseURL}/auth/logout`)
|
|
91
|
+
|
|
92
|
+
// Should succeed (200) or redirect (302)
|
|
93
|
+
expect([200, 302]).toContain(logoutResponse.status())
|
|
94
|
+
|
|
95
|
+
// Verify session is cleared
|
|
96
|
+
const checkResponse = await request.get(`${baseURL}/auth/me`, {
|
|
97
|
+
failOnStatusCode: false,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Should now return 401
|
|
101
|
+
expect(checkResponse.status()).toBe(401)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { test as setup, expect } from '@playwright/test'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const authFile = path.join(__dirname, '.auth/user.json')
|
|
8
|
+
const accountFile = path.join(__dirname, '.auth/account.json')
|
|
9
|
+
|
|
10
|
+
// Production URL pattern - skip test-login for these
|
|
11
|
+
const PRODUCTION_URLS = [
|
|
12
|
+
'{{projectName}}.a3s.workers.dev',
|
|
13
|
+
'workers.dev',
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
function isProductionUrl(baseUrl: string): boolean {
|
|
17
|
+
return PRODUCTION_URLS.some(pattern => baseUrl.includes(pattern))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setup('authenticate', async ({ page, context, baseURL }) => {
|
|
21
|
+
// Check if we're running against production
|
|
22
|
+
if (baseURL && isProductionUrl(baseURL)) {
|
|
23
|
+
console.log(`Running against production: ${baseURL}`)
|
|
24
|
+
|
|
25
|
+
// Check if pre-captured OAuth session exists
|
|
26
|
+
if (fs.existsSync(authFile)) {
|
|
27
|
+
const authState = JSON.parse(fs.readFileSync(authFile, 'utf-8'))
|
|
28
|
+
|
|
29
|
+
// Verify session has cookies (not empty)
|
|
30
|
+
if (authState.cookies?.length > 0) {
|
|
31
|
+
console.log(`Using pre-captured OAuth session with ${authState.cookies.length} cookies`)
|
|
32
|
+
|
|
33
|
+
// Add cookies to context
|
|
34
|
+
await context.addCookies(authState.cookies)
|
|
35
|
+
|
|
36
|
+
// Navigate to set cookies in browser context
|
|
37
|
+
await page.goto('/')
|
|
38
|
+
await page.waitForLoadState('domcontentloaded')
|
|
39
|
+
|
|
40
|
+
// Verify auth works using context.request (shares cookies with browser)
|
|
41
|
+
const meResponse = await context.request.get(`${baseURL}/auth/me`)
|
|
42
|
+
const meStatus = meResponse.status()
|
|
43
|
+
const meBodyText = await meResponse.text()
|
|
44
|
+
console.log(`Auth verification: status=${meStatus}, body=${meBodyText.substring(0, 200)}`)
|
|
45
|
+
|
|
46
|
+
if (meResponse.ok()) {
|
|
47
|
+
const userData = JSON.parse(meBodyText)
|
|
48
|
+
console.log(`Authenticated as: ${userData.email || 'unknown'}`)
|
|
49
|
+
|
|
50
|
+
// Save accountId if available
|
|
51
|
+
if (userData.accounts?.[0]?.id) {
|
|
52
|
+
fs.writeFileSync(accountFile, JSON.stringify({ accountId: userData.accounts[0].id }, null, 2))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Verify dashboard access
|
|
56
|
+
await page.goto('/dashboard')
|
|
57
|
+
await expect(page).not.toHaveURL(/login/)
|
|
58
|
+
|
|
59
|
+
// Re-save auth state (in case cookies were refreshed)
|
|
60
|
+
await context.storageState({ path: authFile })
|
|
61
|
+
return
|
|
62
|
+
} else {
|
|
63
|
+
console.log('Pre-captured session expired or invalid')
|
|
64
|
+
console.log('Run: pnpm test:e2e:prod:auth to capture a new session')
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// No valid pre-captured session for production
|
|
70
|
+
console.log('No valid OAuth session found for production')
|
|
71
|
+
console.log('Run: pnpm test:e2e:prod:auth to capture a session first')
|
|
72
|
+
console.log('Tests requiring authentication will be skipped')
|
|
73
|
+
// DO NOT overwrite auth file in production - preserve any existing captured session
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Development/local: use test-login endpoint
|
|
78
|
+
await page.goto('/')
|
|
79
|
+
|
|
80
|
+
const response = await page.request.post('/auth/test-login', {
|
|
81
|
+
data: {
|
|
82
|
+
email: 'e2e-test@example.com',
|
|
83
|
+
name: 'E2E Test User',
|
|
84
|
+
},
|
|
85
|
+
failOnStatusCode: false,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
if (!response.ok()) {
|
|
89
|
+
const body = await response.text()
|
|
90
|
+
console.log('Test login failed:', response.status(), body)
|
|
91
|
+
console.log('Creating empty auth state - tests will run as unauthenticated')
|
|
92
|
+
await context.storageState({ path: authFile })
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse the response to get the accountId
|
|
97
|
+
const loginData = await response.json()
|
|
98
|
+
const accountId = loginData.accountId
|
|
99
|
+
|
|
100
|
+
// Save accountId to file for API tests
|
|
101
|
+
if (accountId) {
|
|
102
|
+
fs.writeFileSync(accountFile, JSON.stringify({ accountId }, null, 2))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Verify authentication works
|
|
106
|
+
const meResponse = await page.request.get('/auth/me')
|
|
107
|
+
expect(meResponse.ok()).toBeTruthy()
|
|
108
|
+
|
|
109
|
+
// Navigate to dashboard to verify auth works in UI
|
|
110
|
+
await page.goto('/dashboard')
|
|
111
|
+
await expect(page).not.toHaveURL(/login/)
|
|
112
|
+
|
|
113
|
+
// Save authenticated state
|
|
114
|
+
await context.storageState({ path: authFile })
|
|
115
|
+
})
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { test, expect, isAuthenticated, waitForNavigation } from './fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Authenticated User Tests
|
|
5
|
+
*
|
|
6
|
+
* These tests require authentication via auth.setup.ts which creates session state.
|
|
7
|
+
* Since we can't actually authenticate with Google OAuth in E2E tests without credentials,
|
|
8
|
+
* tests will check for session availability and skip if not authenticated.
|
|
9
|
+
*
|
|
10
|
+
* Run with authenticated project: npx playwright test --project=chromium
|
|
11
|
+
*
|
|
12
|
+
* @tags @auth
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
test.describe('Authenticated User @auth', () => {
|
|
16
|
+
test.beforeEach(async ({ page }) => {
|
|
17
|
+
// Check if we have a valid session
|
|
18
|
+
const authenticated = await isAuthenticated(page)
|
|
19
|
+
test.skip(!authenticated, 'No valid session available. Run auth.setup.ts with valid credentials.')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test.describe('Navigation', () => {
|
|
23
|
+
test('should access dashboard when authenticated', async ({ page }) => {
|
|
24
|
+
await page.goto('/dashboard')
|
|
25
|
+
|
|
26
|
+
// Should not be redirected to login
|
|
27
|
+
await expect(page).not.toHaveURL(/login/)
|
|
28
|
+
|
|
29
|
+
// Should see dashboard content
|
|
30
|
+
await expect(page.locator('body')).toBeVisible()
|
|
31
|
+
|
|
32
|
+
// Check for common dashboard elements
|
|
33
|
+
const dashboardIndicators = [
|
|
34
|
+
page.getByRole('heading', { name: /dashboard/i }),
|
|
35
|
+
page.getByText(/welcome/i),
|
|
36
|
+
page.getByRole('navigation'),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
// At least one indicator should be visible
|
|
40
|
+
const results = await Promise.all(
|
|
41
|
+
dashboardIndicators.map(async (locator) => {
|
|
42
|
+
try {
|
|
43
|
+
await expect(locator).toBeVisible({ timeout: 2000 })
|
|
44
|
+
return true
|
|
45
|
+
} catch {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
expect(results.some((r) => r)).toBeTruthy()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('should access settings when authenticated', async ({ page }) => {
|
|
55
|
+
await page.goto('/settings')
|
|
56
|
+
|
|
57
|
+
// Should not be redirected to login
|
|
58
|
+
await expect(page).not.toHaveURL(/login/)
|
|
59
|
+
|
|
60
|
+
// Should see settings page content
|
|
61
|
+
await expect(page.locator('body')).toBeVisible()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('should access team page when authenticated', async ({ page }) => {
|
|
65
|
+
await page.goto('/team')
|
|
66
|
+
|
|
67
|
+
// Should not be redirected to login
|
|
68
|
+
await expect(page).not.toHaveURL(/login/)
|
|
69
|
+
|
|
70
|
+
// Should see team page content
|
|
71
|
+
await expect(page.locator('body')).toBeVisible()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('should navigate between protected routes', async ({ page }) => {
|
|
75
|
+
// Start at dashboard
|
|
76
|
+
await page.goto('/dashboard')
|
|
77
|
+
await expect(page).not.toHaveURL(/login/)
|
|
78
|
+
|
|
79
|
+
// Navigate to settings
|
|
80
|
+
const settingsLink = page.getByRole('link', { name: /settings/i })
|
|
81
|
+
if (await settingsLink.isVisible()) {
|
|
82
|
+
await settingsLink.click()
|
|
83
|
+
await waitForNavigation(page, '/settings')
|
|
84
|
+
await expect(page).toHaveURL(/settings/)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Navigate to team (if link exists)
|
|
88
|
+
const teamLink = page.getByRole('link', { name: /team/i })
|
|
89
|
+
if (await teamLink.isVisible()) {
|
|
90
|
+
await teamLink.click()
|
|
91
|
+
await waitForNavigation(page, '/team')
|
|
92
|
+
await expect(page).toHaveURL(/team/)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test.describe('User Session', () => {
|
|
98
|
+
test('/auth/me should return current user info', async ({ page, request, baseURL }) => {
|
|
99
|
+
// First verify we're authenticated
|
|
100
|
+
const authenticated = await isAuthenticated(page)
|
|
101
|
+
test.skip(!authenticated, 'No valid session available')
|
|
102
|
+
|
|
103
|
+
const response = await request.get(`${baseURL}/auth/me`)
|
|
104
|
+
|
|
105
|
+
expect(response.ok()).toBeTruthy()
|
|
106
|
+
|
|
107
|
+
const body = await response.json()
|
|
108
|
+
|
|
109
|
+
// Should have user data
|
|
110
|
+
expect(body).toHaveProperty('user')
|
|
111
|
+
expect(body.user).toHaveProperty('email')
|
|
112
|
+
expect(body.user).toHaveProperty('name')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('should display user info in UI', async ({ page }) => {
|
|
116
|
+
await page.goto('/dashboard')
|
|
117
|
+
|
|
118
|
+
// Look for user avatar, name, or email indicator
|
|
119
|
+
const userIndicators = [
|
|
120
|
+
page.getByRole('button', { name: /account|profile|user/i }),
|
|
121
|
+
page.getByText(/e2e-test@example.com/i),
|
|
122
|
+
page.getByRole('img', { name: /avatar|profile/i }),
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
// At least one user indicator should be visible
|
|
126
|
+
const results = await Promise.all(
|
|
127
|
+
userIndicators.map(async (locator) => {
|
|
128
|
+
try {
|
|
129
|
+
await expect(locator).toBeVisible({ timeout: 3000 })
|
|
130
|
+
return true
|
|
131
|
+
} catch {
|
|
132
|
+
return false
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
// This test is informational - may pass or fail depending on UI implementation
|
|
138
|
+
if (!results.some((r) => r)) {
|
|
139
|
+
console.log('Note: No user indicator found in UI. This may be expected based on UI design.')
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// NOTE: Logout tests moved to auth-logout.unauth.spec.ts
|
|
145
|
+
// They run in chromium-unauth project to avoid invalidating shared session state
|
|
146
|
+
})
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { test, expect, isAuthenticated, closeAllDialogs } from '../fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cross-Browser Compatibility Tests
|
|
5
|
+
*
|
|
6
|
+
* These tests verify core functionality works consistently
|
|
7
|
+
* across different browsers (Chromium, Firefox, WebKit).
|
|
8
|
+
*
|
|
9
|
+
* Run across browsers: npx playwright test e2e/compatibility --project=chromium --project=firefox
|
|
10
|
+
*
|
|
11
|
+
* @tags @compatibility @cross-browser
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
test.describe('Cross-Browser Compatibility @compatibility @cross-browser', () => {
|
|
15
|
+
test.describe('Public Pages', () => {
|
|
16
|
+
test('login page renders correctly', async ({ page }) => {
|
|
17
|
+
await page.goto('/login')
|
|
18
|
+
|
|
19
|
+
// Core elements should be visible
|
|
20
|
+
await expect(page.getByText('Welcome back')).toBeVisible()
|
|
21
|
+
await expect(page.getByRole('button', { name: /continue with google/i })).toBeVisible()
|
|
22
|
+
|
|
23
|
+
// Verify page structure
|
|
24
|
+
await expect(page.locator('body')).toBeVisible()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('404 page renders correctly', async ({ page, browserName }) => {
|
|
28
|
+
await page.goto('/non-existent-page')
|
|
29
|
+
|
|
30
|
+
// 404 content should be visible
|
|
31
|
+
await expect(page.getByText('404').first()).toBeVisible()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('page navigation works correctly', async ({ page }) => {
|
|
35
|
+
// Navigate to login
|
|
36
|
+
await page.goto('/login')
|
|
37
|
+
await expect(page.getByText('Welcome back')).toBeVisible()
|
|
38
|
+
|
|
39
|
+
// Navigate to 404
|
|
40
|
+
await page.goto('/unknown')
|
|
41
|
+
await expect(page.getByText('404').first()).toBeVisible()
|
|
42
|
+
|
|
43
|
+
// Navigate back to login
|
|
44
|
+
await page.goto('/login')
|
|
45
|
+
await expect(page.getByText('Welcome back')).toBeVisible()
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test.describe('Authenticated Pages', () => {
|
|
50
|
+
test.beforeEach(async ({ page }) => {
|
|
51
|
+
const authenticated = await isAuthenticated(page)
|
|
52
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test.afterEach(async ({ page }) => {
|
|
56
|
+
await closeAllDialogs(page)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('dashboard renders correctly', async ({ page, browserName }) => {
|
|
60
|
+
await page.goto('/dashboard')
|
|
61
|
+
|
|
62
|
+
// Navigation should be visible
|
|
63
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
64
|
+
|
|
65
|
+
// Main content area should be visible
|
|
66
|
+
await expect(page.getByRole('main')).toBeVisible()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('team page renders correctly', async ({ page }) => {
|
|
70
|
+
await page.goto('/team')
|
|
71
|
+
|
|
72
|
+
// Team heading should be visible
|
|
73
|
+
await expect(page.getByRole('heading', { name: /team members/i })).toBeVisible()
|
|
74
|
+
|
|
75
|
+
// Invite button should be visible and functional
|
|
76
|
+
const inviteButton = page.locator('button').filter({ hasText: 'Invite Member' }).first()
|
|
77
|
+
await expect(inviteButton).toBeVisible()
|
|
78
|
+
await expect(inviteButton).toBeEnabled()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('form interactions work correctly', async ({ page }) => {
|
|
82
|
+
await page.goto('/team')
|
|
83
|
+
|
|
84
|
+
// Open invite dialog
|
|
85
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
86
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
87
|
+
|
|
88
|
+
// Form input should work
|
|
89
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
90
|
+
await emailInput.fill('test@example.com')
|
|
91
|
+
await expect(emailInput).toHaveValue('test@example.com')
|
|
92
|
+
|
|
93
|
+
// Clear input
|
|
94
|
+
await emailInput.clear()
|
|
95
|
+
await expect(emailInput).toHaveValue('')
|
|
96
|
+
|
|
97
|
+
// Close dialog
|
|
98
|
+
await page.getByRole('button', { name: /cancel/i }).click()
|
|
99
|
+
await expect(page.getByRole('dialog')).not.toBeVisible()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('dropdown and select interactions work correctly', async ({ page }) => {
|
|
103
|
+
await page.goto('/team')
|
|
104
|
+
|
|
105
|
+
// Open invite dialog
|
|
106
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
107
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
108
|
+
|
|
109
|
+
// Role selector should be visible (look for the select trigger or role text)
|
|
110
|
+
const roleText = page.getByText(/role/i).first()
|
|
111
|
+
await expect(roleText).toBeVisible()
|
|
112
|
+
|
|
113
|
+
// Close dialog
|
|
114
|
+
await page.getByRole('button', { name: /cancel/i }).click()
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('button states and interactions work correctly', async ({ page }) => {
|
|
118
|
+
await page.goto('/team')
|
|
119
|
+
|
|
120
|
+
// Open invite dialog
|
|
121
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
122
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
123
|
+
|
|
124
|
+
// Send button should be disabled without email
|
|
125
|
+
const sendButton = page.getByRole('button', { name: /send invitation/i })
|
|
126
|
+
await expect(sendButton).toBeDisabled()
|
|
127
|
+
|
|
128
|
+
// Fill email to enable button
|
|
129
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
130
|
+
await emailInput.fill('valid@email.com')
|
|
131
|
+
await expect(sendButton).toBeEnabled()
|
|
132
|
+
|
|
133
|
+
// Close dialog
|
|
134
|
+
await page.getByRole('button', { name: /cancel/i }).click()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('keyboard navigation works correctly', async ({ page }) => {
|
|
138
|
+
await page.goto('/settings')
|
|
139
|
+
|
|
140
|
+
// Verify tabs are visible and accessible
|
|
141
|
+
const profileTab = page.getByRole('tab', { name: /profile/i })
|
|
142
|
+
await expect(profileTab).toBeVisible()
|
|
143
|
+
|
|
144
|
+
// Click profile tab to ensure focus is in tab area
|
|
145
|
+
await profileTab.click()
|
|
146
|
+
|
|
147
|
+
// Navigate to account tab
|
|
148
|
+
await page.getByRole('tab', { name: /account/i }).click()
|
|
149
|
+
await expect(page.getByRole('tab', { name: /account/i })).toHaveAttribute('data-state', 'active')
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
})
|