@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,569 @@
|
|
|
1
|
+
import { test, expect, isAuthenticated, closeAllDialogs } from '../fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Form Validation E2E Tests
|
|
5
|
+
*
|
|
6
|
+
* These tests verify form validation behaviors using React Hook Form with Zod:
|
|
7
|
+
* - Validation on blur (not just submit)
|
|
8
|
+
* - Form reset functionality
|
|
9
|
+
* - Keyboard navigation for select/dropdown-like controls
|
|
10
|
+
* - Inline error message display
|
|
11
|
+
* - Loading state with spinner on submit
|
|
12
|
+
*
|
|
13
|
+
* @tags @forms @validation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
test.describe('Form Validation @forms @validation', () => {
|
|
17
|
+
test.beforeEach(async ({ page }) => {
|
|
18
|
+
const authenticated = await isAuthenticated(page)
|
|
19
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test.afterEach(async ({ page }) => {
|
|
23
|
+
await closeAllDialogs(page)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test.describe('Validation on Blur', () => {
|
|
27
|
+
test('should show validation error on blur for invalid email in team invite dialog', async ({ page }) => {
|
|
28
|
+
await page.goto('/team')
|
|
29
|
+
|
|
30
|
+
// Open invite dialog
|
|
31
|
+
const inviteButton = page.locator('button').filter({ hasText: 'Invite Member' }).first()
|
|
32
|
+
await expect(inviteButton).toBeVisible()
|
|
33
|
+
await inviteButton.click()
|
|
34
|
+
|
|
35
|
+
// Wait for dialog to open
|
|
36
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
37
|
+
|
|
38
|
+
// Get the email input
|
|
39
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
40
|
+
await expect(emailInput).toBeVisible()
|
|
41
|
+
|
|
42
|
+
// Type an invalid email
|
|
43
|
+
await emailInput.fill('invalid-email')
|
|
44
|
+
|
|
45
|
+
// Blur the input by clicking elsewhere (on the role label)
|
|
46
|
+
await page.getByText('Role').click()
|
|
47
|
+
|
|
48
|
+
// Wait a moment for validation to trigger
|
|
49
|
+
await page.waitForTimeout(100)
|
|
50
|
+
|
|
51
|
+
// Check if error message appears (either inline or after submit)
|
|
52
|
+
// React Hook Form with zodResolver typically validates on blur when mode: 'onBlur' is set
|
|
53
|
+
// The default mode is 'onSubmit', but we still want to verify the pattern works
|
|
54
|
+
|
|
55
|
+
// Try to submit with invalid email to trigger validation
|
|
56
|
+
const sendButton = page.getByRole('button', { name: /send invitation/i })
|
|
57
|
+
await sendButton.click()
|
|
58
|
+
|
|
59
|
+
// Now error should be visible
|
|
60
|
+
const errorMessage = page.locator('text=/email|invalid|required/i').first()
|
|
61
|
+
await expect(errorMessage).toBeVisible({ timeout: 3000 })
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('should show validation error when email is empty on form submit', async ({ page }) => {
|
|
65
|
+
await page.goto('/team')
|
|
66
|
+
|
|
67
|
+
// Open invite dialog
|
|
68
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
69
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
70
|
+
|
|
71
|
+
// Get the email input and clear it
|
|
72
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
73
|
+
await emailInput.clear()
|
|
74
|
+
|
|
75
|
+
// Focus and then blur the input
|
|
76
|
+
await emailInput.focus()
|
|
77
|
+
await emailInput.blur()
|
|
78
|
+
|
|
79
|
+
// Click Send button to trigger validation
|
|
80
|
+
const sendButton = page.getByRole('button', { name: /send invitation/i })
|
|
81
|
+
await sendButton.click()
|
|
82
|
+
|
|
83
|
+
// Verify error message appears for empty email
|
|
84
|
+
await expect(page.locator('.text-destructive').first()).toBeVisible({ timeout: 3000 })
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('should validate webhook URL format in integrations page', async ({ page }) => {
|
|
88
|
+
await page.goto('/integrations')
|
|
89
|
+
|
|
90
|
+
// Open create webhook dialog
|
|
91
|
+
const addWebhookButton = page.getByRole('button', { name: /add webhook/i })
|
|
92
|
+
await expect(addWebhookButton).toBeVisible()
|
|
93
|
+
await addWebhookButton.click()
|
|
94
|
+
|
|
95
|
+
// Wait for dialog
|
|
96
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
97
|
+
await expect(page.getByRole('heading', { name: /create webhook/i })).toBeVisible()
|
|
98
|
+
|
|
99
|
+
// Enter an invalid URL
|
|
100
|
+
const urlInput = page.getByPlaceholder('https://api.example.com/webhooks')
|
|
101
|
+
await expect(urlInput).toBeVisible()
|
|
102
|
+
await urlInput.fill('not-a-valid-url')
|
|
103
|
+
|
|
104
|
+
// Blur and try to submit
|
|
105
|
+
await urlInput.blur()
|
|
106
|
+
const createButton = page.getByRole('button', { name: /create webhook/i })
|
|
107
|
+
await createButton.click()
|
|
108
|
+
|
|
109
|
+
// Should show validation error
|
|
110
|
+
await expect(page.locator('.text-destructive').first()).toBeVisible({ timeout: 3000 })
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test.describe('Form Reset', () => {
|
|
115
|
+
test('should clear all fields and errors when cancel is clicked on team invite dialog', async ({ page }) => {
|
|
116
|
+
await page.goto('/team')
|
|
117
|
+
|
|
118
|
+
// Open invite dialog
|
|
119
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
120
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
121
|
+
|
|
122
|
+
// Fill in the email input
|
|
123
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
124
|
+
await emailInput.fill('test@example.com')
|
|
125
|
+
|
|
126
|
+
// Select Admin role
|
|
127
|
+
const adminButton = page.getByRole('button', { name: /^admin$/i })
|
|
128
|
+
await adminButton.click()
|
|
129
|
+
|
|
130
|
+
// Verify fields are filled
|
|
131
|
+
await expect(emailInput).toHaveValue('test@example.com')
|
|
132
|
+
await expect(adminButton).toHaveClass(/default/)
|
|
133
|
+
|
|
134
|
+
// Click Cancel
|
|
135
|
+
const cancelButton = page.getByRole('button', { name: /cancel/i })
|
|
136
|
+
await cancelButton.click()
|
|
137
|
+
|
|
138
|
+
// Dialog should close
|
|
139
|
+
await expect(page.getByRole('dialog')).not.toBeVisible()
|
|
140
|
+
|
|
141
|
+
// Re-open the dialog
|
|
142
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
143
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
144
|
+
|
|
145
|
+
// Verify fields are reset
|
|
146
|
+
const newEmailInput = page.getByLabel(/email address/i)
|
|
147
|
+
await expect(newEmailInput).toHaveValue('')
|
|
148
|
+
|
|
149
|
+
// Member should be selected by default (not Admin)
|
|
150
|
+
const memberButton = page.getByRole('button', { name: /^member$/i })
|
|
151
|
+
await expect(memberButton).toHaveClass(/default/)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('should clear webhook form when dialog is closed', async ({ page }) => {
|
|
155
|
+
await page.goto('/integrations')
|
|
156
|
+
|
|
157
|
+
// Open create webhook dialog
|
|
158
|
+
await page.getByRole('button', { name: /add webhook/i }).click()
|
|
159
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
160
|
+
|
|
161
|
+
// Fill in the URL
|
|
162
|
+
const urlInput = page.getByPlaceholder('https://api.example.com/webhooks')
|
|
163
|
+
await urlInput.fill('https://example.com/webhook')
|
|
164
|
+
|
|
165
|
+
// Select an event
|
|
166
|
+
const userCreatedEvent = page.getByRole('button', { name: /user created/i })
|
|
167
|
+
await userCreatedEvent.click()
|
|
168
|
+
|
|
169
|
+
// Verify event is selected (should have primary styling)
|
|
170
|
+
await expect(userCreatedEvent).toHaveClass(/border-primary/)
|
|
171
|
+
|
|
172
|
+
// Click Cancel
|
|
173
|
+
await page.getByRole('button', { name: /cancel/i }).click()
|
|
174
|
+
await expect(page.getByRole('dialog')).not.toBeVisible()
|
|
175
|
+
|
|
176
|
+
// Re-open dialog
|
|
177
|
+
await page.getByRole('button', { name: /add webhook/i }).click()
|
|
178
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
179
|
+
|
|
180
|
+
// Verify form is reset
|
|
181
|
+
const newUrlInput = page.getByPlaceholder('https://api.example.com/webhooks')
|
|
182
|
+
await expect(newUrlInput).toHaveValue('')
|
|
183
|
+
|
|
184
|
+
// Event should not be selected
|
|
185
|
+
const newUserCreatedEvent = page.getByRole('button', { name: /user created/i })
|
|
186
|
+
await expect(newUserCreatedEvent).not.toHaveClass(/border-primary/)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('should reset profile form values after page reload', async ({ page }) => {
|
|
190
|
+
await page.goto('/settings')
|
|
191
|
+
|
|
192
|
+
// Get the name input
|
|
193
|
+
const nameInput = page.getByLabel(/full name/i)
|
|
194
|
+
await expect(nameInput).toBeVisible()
|
|
195
|
+
|
|
196
|
+
// Store original value
|
|
197
|
+
const originalValue = await nameInput.inputValue()
|
|
198
|
+
|
|
199
|
+
// Modify the value
|
|
200
|
+
await nameInput.clear()
|
|
201
|
+
await nameInput.fill('Test Modified Name')
|
|
202
|
+
await expect(nameInput).toHaveValue('Test Modified Name')
|
|
203
|
+
|
|
204
|
+
// Reload the page (should reset to saved value)
|
|
205
|
+
await page.reload()
|
|
206
|
+
|
|
207
|
+
// Verify name is reset to original value (from server/user data)
|
|
208
|
+
const reloadedNameInput = page.getByLabel(/full name/i)
|
|
209
|
+
await expect(reloadedNameInput).toHaveValue(originalValue)
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test.describe('Keyboard Navigation', () => {
|
|
214
|
+
test('should navigate through form fields with Tab key', async ({ page }) => {
|
|
215
|
+
await page.goto('/team')
|
|
216
|
+
|
|
217
|
+
// Open invite dialog
|
|
218
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
219
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
220
|
+
|
|
221
|
+
// Focus the email input
|
|
222
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
223
|
+
await emailInput.focus()
|
|
224
|
+
await expect(emailInput).toBeFocused()
|
|
225
|
+
|
|
226
|
+
// Tab to next focusable element (Member button)
|
|
227
|
+
await page.keyboard.press('Tab')
|
|
228
|
+
const memberButton = page.getByRole('button', { name: /^member$/i })
|
|
229
|
+
await expect(memberButton).toBeFocused()
|
|
230
|
+
|
|
231
|
+
// Tab to Admin button
|
|
232
|
+
await page.keyboard.press('Tab')
|
|
233
|
+
const adminButton = page.getByRole('button', { name: /^admin$/i })
|
|
234
|
+
await expect(adminButton).toBeFocused()
|
|
235
|
+
|
|
236
|
+
// Tab to Cancel button
|
|
237
|
+
await page.keyboard.press('Tab')
|
|
238
|
+
const cancelButton = page.getByRole('button', { name: /cancel/i })
|
|
239
|
+
await expect(cancelButton).toBeFocused()
|
|
240
|
+
|
|
241
|
+
// Tab to Send Invitation button
|
|
242
|
+
await page.keyboard.press('Tab')
|
|
243
|
+
const sendButton = page.getByRole('button', { name: /send invitation/i })
|
|
244
|
+
await expect(sendButton).toBeFocused()
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
test('should activate role buttons with Enter key', async ({ page }) => {
|
|
248
|
+
await page.goto('/team')
|
|
249
|
+
|
|
250
|
+
// Open invite dialog
|
|
251
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
252
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
253
|
+
|
|
254
|
+
// Member should be selected by default
|
|
255
|
+
const memberButton = page.getByRole('button', { name: /^member$/i })
|
|
256
|
+
const adminButton = page.getByRole('button', { name: /^admin$/i })
|
|
257
|
+
await expect(memberButton).toHaveClass(/default/)
|
|
258
|
+
|
|
259
|
+
// Focus Admin button and press Enter
|
|
260
|
+
await adminButton.focus()
|
|
261
|
+
await page.keyboard.press('Enter')
|
|
262
|
+
|
|
263
|
+
// Admin should now be selected
|
|
264
|
+
await expect(adminButton).toHaveClass(/default/)
|
|
265
|
+
await expect(memberButton).not.toHaveClass(/default/)
|
|
266
|
+
|
|
267
|
+
// Verify role description changed
|
|
268
|
+
await expect(page.getByText(/admins can manage team settings/i)).toBeVisible()
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
test('should activate role buttons with Space key', async ({ page }) => {
|
|
272
|
+
await page.goto('/team')
|
|
273
|
+
|
|
274
|
+
// Open invite dialog
|
|
275
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
276
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
277
|
+
|
|
278
|
+
const memberButton = page.getByRole('button', { name: /^member$/i })
|
|
279
|
+
const adminButton = page.getByRole('button', { name: /^admin$/i })
|
|
280
|
+
|
|
281
|
+
// Focus Admin button and press Space
|
|
282
|
+
await adminButton.focus()
|
|
283
|
+
await page.keyboard.press(' ')
|
|
284
|
+
|
|
285
|
+
// Admin should now be selected
|
|
286
|
+
await expect(adminButton).toHaveClass(/default/)
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
test('should close dialog with Escape key', async ({ page }) => {
|
|
290
|
+
await page.goto('/team')
|
|
291
|
+
|
|
292
|
+
// Open invite dialog
|
|
293
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
294
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
295
|
+
|
|
296
|
+
// Press Escape
|
|
297
|
+
await page.keyboard.press('Escape')
|
|
298
|
+
|
|
299
|
+
// Dialog should be closed
|
|
300
|
+
await expect(page.getByRole('dialog')).not.toBeVisible()
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
test('should toggle webhook events with keyboard', async ({ page }) => {
|
|
304
|
+
await page.goto('/integrations')
|
|
305
|
+
|
|
306
|
+
// Open create webhook dialog
|
|
307
|
+
await page.getByRole('button', { name: /add webhook/i }).click()
|
|
308
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
309
|
+
|
|
310
|
+
// Find and focus the first event button
|
|
311
|
+
const userCreatedButton = page.getByRole('button', { name: /user created/i })
|
|
312
|
+
await userCreatedButton.focus()
|
|
313
|
+
|
|
314
|
+
// Press Enter to select
|
|
315
|
+
await page.keyboard.press('Enter')
|
|
316
|
+
await expect(userCreatedButton).toHaveClass(/border-primary/)
|
|
317
|
+
|
|
318
|
+
// Press Enter again to deselect
|
|
319
|
+
await page.keyboard.press('Enter')
|
|
320
|
+
await expect(userCreatedButton).not.toHaveClass(/border-primary/)
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
test.describe('Inline Error Messages', () => {
|
|
325
|
+
test('should display error message near the invalid email field', async ({ page }) => {
|
|
326
|
+
await page.goto('/team')
|
|
327
|
+
|
|
328
|
+
// Open invite dialog
|
|
329
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
330
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
331
|
+
|
|
332
|
+
// Submit with empty email
|
|
333
|
+
const sendButton = page.getByRole('button', { name: /send invitation/i })
|
|
334
|
+
await sendButton.click()
|
|
335
|
+
|
|
336
|
+
// Find the email form item container
|
|
337
|
+
const emailFormItem = page.locator('[class*="space-y"]').filter({ has: page.getByLabel(/email address/i) }).first()
|
|
338
|
+
|
|
339
|
+
// Verify error message is within the form item (inline)
|
|
340
|
+
const errorMessage = emailFormItem.locator('.text-destructive')
|
|
341
|
+
await expect(errorMessage).toBeVisible()
|
|
342
|
+
|
|
343
|
+
// Verify error message is positioned after the input (inline near field)
|
|
344
|
+
const inputBoundingBox = await page.getByLabel(/email address/i).boundingBox()
|
|
345
|
+
const errorBoundingBox = await errorMessage.boundingBox()
|
|
346
|
+
|
|
347
|
+
// Error should be below or near the input (y position should be greater or close)
|
|
348
|
+
if (inputBoundingBox && errorBoundingBox) {
|
|
349
|
+
expect(errorBoundingBox.y).toBeGreaterThanOrEqual(inputBoundingBox.y)
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
test('should display validation error with descriptive message', async ({ page }) => {
|
|
354
|
+
await page.goto('/team')
|
|
355
|
+
|
|
356
|
+
// Open invite dialog
|
|
357
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
358
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
359
|
+
|
|
360
|
+
// Enter invalid email format
|
|
361
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
362
|
+
await emailInput.fill('not-an-email')
|
|
363
|
+
|
|
364
|
+
// Submit form
|
|
365
|
+
await page.getByRole('button', { name: /send invitation/i }).click()
|
|
366
|
+
|
|
367
|
+
// Check for descriptive error message
|
|
368
|
+
const errorMessage = page.locator('.text-destructive').first()
|
|
369
|
+
await expect(errorMessage).toBeVisible()
|
|
370
|
+
|
|
371
|
+
// Error text should contain helpful guidance
|
|
372
|
+
const errorText = await errorMessage.textContent()
|
|
373
|
+
expect(errorText).toBeTruthy()
|
|
374
|
+
// Should mention email is invalid
|
|
375
|
+
expect(errorText?.toLowerCase()).toMatch(/email|invalid|required/)
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
test('should display error for empty required webhook URL', async ({ page }) => {
|
|
379
|
+
await page.goto('/integrations')
|
|
380
|
+
|
|
381
|
+
// Open create webhook dialog
|
|
382
|
+
await page.getByRole('button', { name: /add webhook/i }).click()
|
|
383
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
384
|
+
|
|
385
|
+
// Leave URL empty and try to submit
|
|
386
|
+
await page.getByRole('button', { name: /create webhook/i }).click()
|
|
387
|
+
|
|
388
|
+
// Should show error for required URL field
|
|
389
|
+
const errorMessage = page.locator('.text-destructive').first()
|
|
390
|
+
await expect(errorMessage).toBeVisible()
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
test('should clear error message when valid input is provided', async ({ page }) => {
|
|
394
|
+
await page.goto('/team')
|
|
395
|
+
|
|
396
|
+
// Open invite dialog
|
|
397
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
398
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
399
|
+
|
|
400
|
+
// Submit with empty email to trigger error
|
|
401
|
+
await page.getByRole('button', { name: /send invitation/i }).click()
|
|
402
|
+
|
|
403
|
+
// Error should be visible
|
|
404
|
+
const errorMessage = page.locator('.text-destructive').first()
|
|
405
|
+
await expect(errorMessage).toBeVisible()
|
|
406
|
+
|
|
407
|
+
// Now enter a valid email
|
|
408
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
409
|
+
await emailInput.fill('valid@example.com')
|
|
410
|
+
|
|
411
|
+
// Blur to trigger re-validation
|
|
412
|
+
await emailInput.blur()
|
|
413
|
+
|
|
414
|
+
// Error should be cleared after providing valid input and blurring
|
|
415
|
+
// (depends on validation mode, may need to submit again)
|
|
416
|
+
await page.getByRole('button', { name: /send invitation/i }).click()
|
|
417
|
+
|
|
418
|
+
// Wait for form submission (if validation passes, button will show loading or dialog closes)
|
|
419
|
+
// If error persists, it means email was still invalid
|
|
420
|
+
// For valid email, we expect either success toast or loading state
|
|
421
|
+
await expect(page.getByRole('button', { name: /send invitation/i })).toBeDisabled({ timeout: 2000 }).catch(() => {
|
|
422
|
+
// If button is not disabled, check if error is gone
|
|
423
|
+
// (form may submit successfully)
|
|
424
|
+
})
|
|
425
|
+
})
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
test.describe('Loading State', () => {
|
|
429
|
+
test('should show spinner on submit button during form submission in team invite', async ({ page }) => {
|
|
430
|
+
await page.goto('/team')
|
|
431
|
+
|
|
432
|
+
// Open invite dialog
|
|
433
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
434
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
435
|
+
|
|
436
|
+
// Fill in valid email
|
|
437
|
+
const emailInput = page.getByLabel(/email address/i)
|
|
438
|
+
await emailInput.fill('test-invite@example.com')
|
|
439
|
+
|
|
440
|
+
// Click Send Invitation
|
|
441
|
+
const sendButton = page.getByRole('button', { name: /send invitation/i })
|
|
442
|
+
await sendButton.click()
|
|
443
|
+
|
|
444
|
+
// Button should be disabled and show spinner during submission
|
|
445
|
+
await expect(sendButton).toBeDisabled()
|
|
446
|
+
|
|
447
|
+
// Look for spinner icon (animate-spin class)
|
|
448
|
+
const spinner = sendButton.locator('[class*="animate-spin"]')
|
|
449
|
+
await expect(spinner).toBeVisible({ timeout: 2000 }).catch(() => {
|
|
450
|
+
// Spinner may be too fast to catch, check if button was at least disabled
|
|
451
|
+
})
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
test('should show spinner on submit button during webhook creation', async ({ page }) => {
|
|
455
|
+
await page.goto('/integrations')
|
|
456
|
+
|
|
457
|
+
// Open create webhook dialog
|
|
458
|
+
await page.getByRole('button', { name: /add webhook/i }).click()
|
|
459
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
460
|
+
|
|
461
|
+
// Fill in valid URL
|
|
462
|
+
const urlInput = page.getByPlaceholder('https://api.example.com/webhooks')
|
|
463
|
+
await urlInput.fill('https://example.com/webhook')
|
|
464
|
+
|
|
465
|
+
// Select at least one event
|
|
466
|
+
await page.getByRole('button', { name: /user created/i }).click()
|
|
467
|
+
|
|
468
|
+
// Click Create Webhook
|
|
469
|
+
const createButton = page.getByRole('button', { name: /create webhook/i })
|
|
470
|
+
await createButton.click()
|
|
471
|
+
|
|
472
|
+
// Button should be disabled during submission
|
|
473
|
+
await expect(createButton).toBeDisabled()
|
|
474
|
+
|
|
475
|
+
// Look for spinner or loading text
|
|
476
|
+
const loadingState = createButton.locator('[class*="animate-spin"]')
|
|
477
|
+
await expect(loadingState).toBeVisible().catch(() => {
|
|
478
|
+
// Check for "Creating..." text if spinner not visible
|
|
479
|
+
expect(createButton).toContainText(/creating/i)
|
|
480
|
+
})
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
test('should show spinner on profile save button during submission', async ({ page }) => {
|
|
484
|
+
await page.goto('/settings')
|
|
485
|
+
|
|
486
|
+
// Get the name input and modify it
|
|
487
|
+
const nameInput = page.getByLabel(/full name/i)
|
|
488
|
+
await expect(nameInput).toBeVisible()
|
|
489
|
+
|
|
490
|
+
// Store original and set new value
|
|
491
|
+
const originalName = await nameInput.inputValue()
|
|
492
|
+
await nameInput.clear()
|
|
493
|
+
await nameInput.fill('Test Loading State')
|
|
494
|
+
|
|
495
|
+
// Click Save Changes
|
|
496
|
+
const saveButton = page.getByRole('button', { name: /save changes/i })
|
|
497
|
+
await saveButton.click()
|
|
498
|
+
|
|
499
|
+
// Button should be disabled during submission
|
|
500
|
+
await expect(saveButton).toBeDisabled()
|
|
501
|
+
|
|
502
|
+
// Check for spinner
|
|
503
|
+
const spinner = saveButton.locator('[class*="animate-spin"]')
|
|
504
|
+
await expect(spinner).toBeVisible().catch(() => {
|
|
505
|
+
// Spinner may be too fast, just verify button was disabled
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
// Wait for save to complete and restore
|
|
509
|
+
await expect(saveButton).toBeEnabled({ timeout: 5000 })
|
|
510
|
+
|
|
511
|
+
// Restore original name
|
|
512
|
+
await nameInput.clear()
|
|
513
|
+
await nameInput.fill(originalName || '')
|
|
514
|
+
await saveButton.click()
|
|
515
|
+
await expect(saveButton).toBeEnabled({ timeout: 5000 })
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
test('should disable submit button while submitting to prevent double submission', async ({ page }) => {
|
|
519
|
+
await page.goto('/team')
|
|
520
|
+
|
|
521
|
+
// Open invite dialog
|
|
522
|
+
await page.locator('button').filter({ hasText: 'Invite Member' }).first().click()
|
|
523
|
+
await expect(page.getByRole('dialog')).toBeVisible()
|
|
524
|
+
|
|
525
|
+
// Fill in valid email
|
|
526
|
+
await page.getByLabel(/email address/i).fill('prevent-double@example.com')
|
|
527
|
+
|
|
528
|
+
// Click Send Invitation
|
|
529
|
+
const sendButton = page.getByRole('button', { name: /send invitation/i })
|
|
530
|
+
await sendButton.click()
|
|
531
|
+
|
|
532
|
+
// Immediately try to click again - should be disabled
|
|
533
|
+
const isDisabledDuringSubmit = await sendButton.isDisabled()
|
|
534
|
+
expect(isDisabledDuringSubmit).toBe(true)
|
|
535
|
+
|
|
536
|
+
// Wait for submission to complete
|
|
537
|
+
await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 5000 })
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
test('should re-enable submit button after submission completes', async ({ page }) => {
|
|
541
|
+
await page.goto('/settings')
|
|
542
|
+
|
|
543
|
+
// Modify the name
|
|
544
|
+
const nameInput = page.getByLabel(/full name/i)
|
|
545
|
+
await expect(nameInput).toBeVisible()
|
|
546
|
+
|
|
547
|
+
const originalName = await nameInput.inputValue()
|
|
548
|
+
await nameInput.clear()
|
|
549
|
+
await nameInput.fill('Test Re-enable Button')
|
|
550
|
+
|
|
551
|
+
// Submit
|
|
552
|
+
const saveButton = page.getByRole('button', { name: /save changes/i })
|
|
553
|
+
await saveButton.click()
|
|
554
|
+
|
|
555
|
+
// Wait for button to become disabled then enabled again
|
|
556
|
+
await expect(saveButton).toBeDisabled()
|
|
557
|
+
await expect(saveButton).toBeEnabled({ timeout: 5000 })
|
|
558
|
+
|
|
559
|
+
// Button should be clickable again
|
|
560
|
+
await expect(saveButton).not.toBeDisabled()
|
|
561
|
+
|
|
562
|
+
// Restore original name
|
|
563
|
+
await nameInput.clear()
|
|
564
|
+
await nameInput.fill(originalName || '')
|
|
565
|
+
await saveButton.click()
|
|
566
|
+
await expect(saveButton).toBeEnabled({ timeout: 5000 })
|
|
567
|
+
})
|
|
568
|
+
})
|
|
569
|
+
})
|