@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,428 @@
|
|
|
1
|
+
import { test, expect, isAuthenticated } from '../fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error Boundary E2E Tests
|
|
5
|
+
*
|
|
6
|
+
* Tests React error boundaries, network error handling,
|
|
7
|
+
* and error recovery UI patterns.
|
|
8
|
+
*
|
|
9
|
+
* @tags @error @error-boundary
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
test.describe('Error Boundary @error @error-boundary', () => {
|
|
13
|
+
test.describe('Route Error Handling', () => {
|
|
14
|
+
test.beforeEach(async ({ page }) => {
|
|
15
|
+
const authenticated = await isAuthenticated(page)
|
|
16
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('error boundary shows fallback UI when route loader fails', async ({ page }) => {
|
|
20
|
+
// Intercept auth/me to return 500 error (simulates route loader failure)
|
|
21
|
+
await page.route('**/auth/me', async (route) => {
|
|
22
|
+
// First let it succeed to establish session, then fail on navigation
|
|
23
|
+
const response = await route.fetch()
|
|
24
|
+
await route.fulfill({ response })
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Navigate to dashboard first
|
|
28
|
+
await page.goto('/dashboard')
|
|
29
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
30
|
+
|
|
31
|
+
// Now intercept API calls to cause loader failure
|
|
32
|
+
await page.route('**/api/audits**', (route) => {
|
|
33
|
+
route.fulfill({
|
|
34
|
+
status: 500,
|
|
35
|
+
contentType: 'application/json',
|
|
36
|
+
body: JSON.stringify({ error: { message: 'Internal Server Error' } }),
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Page should still function - errors are handled gracefully
|
|
41
|
+
await expect(page).toHaveURL(/dashboard/)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('error fallback shows "Something went wrong" message', async ({ page }) => {
|
|
45
|
+
// Navigate to authenticated page
|
|
46
|
+
await page.goto('/dashboard')
|
|
47
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
48
|
+
|
|
49
|
+
// Intercept the team API to fail with 500
|
|
50
|
+
await page.route('**/api/users**', (route) => {
|
|
51
|
+
route.fulfill({
|
|
52
|
+
status: 500,
|
|
53
|
+
contentType: 'application/json',
|
|
54
|
+
body: JSON.stringify({ error: { message: 'Internal Server Error' } }),
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Navigate to team page which should trigger error UI
|
|
59
|
+
await page.goto('/team')
|
|
60
|
+
|
|
61
|
+
// The page should either show error UI or handle gracefully
|
|
62
|
+
// Check for error indicators or normal page load
|
|
63
|
+
const hasError = await page.getByText(/something went wrong|error|try again/i).isVisible().catch(() => false)
|
|
64
|
+
const hasTeamContent = await page.getByRole('heading', { name: /team members/i }).isVisible().catch(() => false)
|
|
65
|
+
|
|
66
|
+
// Should show either error state or graceful degradation
|
|
67
|
+
expect(hasError || hasTeamContent).toBeTruthy()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('error fallback has "Try again" button that resets state', async ({ page }) => {
|
|
71
|
+
// Navigate to authenticated page
|
|
72
|
+
await page.goto('/dashboard')
|
|
73
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
74
|
+
|
|
75
|
+
let requestCount = 0
|
|
76
|
+
|
|
77
|
+
// Intercept API to fail first, then succeed
|
|
78
|
+
await page.route('**/api/users**', (route) => {
|
|
79
|
+
requestCount++
|
|
80
|
+
if (requestCount <= 1) {
|
|
81
|
+
route.fulfill({
|
|
82
|
+
status: 500,
|
|
83
|
+
contentType: 'application/json',
|
|
84
|
+
body: JSON.stringify({ error: { message: 'Internal Server Error' } }),
|
|
85
|
+
})
|
|
86
|
+
} else {
|
|
87
|
+
// Let subsequent requests through
|
|
88
|
+
route.continue()
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Navigate to team page
|
|
93
|
+
await page.goto('/team')
|
|
94
|
+
|
|
95
|
+
// Look for try again button if error is shown
|
|
96
|
+
const tryAgainButton = page.getByRole('button', { name: /try again/i })
|
|
97
|
+
const hasButton = await tryAgainButton.isVisible({ timeout: 3000 }).catch(() => false)
|
|
98
|
+
|
|
99
|
+
if (hasButton) {
|
|
100
|
+
// Click try again - should reset error state
|
|
101
|
+
await tryAgainButton.click()
|
|
102
|
+
|
|
103
|
+
// Should either reload or show content
|
|
104
|
+
await expect(page).toHaveURL(/team/)
|
|
105
|
+
} else {
|
|
106
|
+
// Error was handled gracefully without showing error UI
|
|
107
|
+
// Verify page loaded
|
|
108
|
+
await expect(page).toHaveURL(/team/)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test.describe('Network Error Handling', () => {
|
|
114
|
+
test.beforeEach(async ({ page }) => {
|
|
115
|
+
const authenticated = await isAuthenticated(page)
|
|
116
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('network errors show user-friendly message with retry option', async ({ page }) => {
|
|
120
|
+
// First navigate to a working page
|
|
121
|
+
await page.goto('/dashboard')
|
|
122
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
123
|
+
|
|
124
|
+
// Intercept API call and abort (simulates network failure)
|
|
125
|
+
await page.route('**/api/users**', (route) => {
|
|
126
|
+
route.abort('failed')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Navigate to team page
|
|
130
|
+
await page.goto('/team')
|
|
131
|
+
|
|
132
|
+
// Should show error state or handle gracefully
|
|
133
|
+
// Look for error indicators
|
|
134
|
+
const errorText = page.getByText(/error|failed|network|offline|try again/i)
|
|
135
|
+
const hasError = await errorText.first().isVisible({ timeout: 3000 }).catch(() => false)
|
|
136
|
+
|
|
137
|
+
// If error is shown, look for retry option
|
|
138
|
+
if (hasError) {
|
|
139
|
+
const retryButton = page.getByRole('button', { name: /try again|retry|reload/i })
|
|
140
|
+
const hasRetry = await retryButton.isVisible({ timeout: 2000 }).catch(() => false)
|
|
141
|
+
|
|
142
|
+
// Error UI should have a retry mechanism
|
|
143
|
+
expect(hasRetry || page.getByRole('link', { name: /home|dashboard|back/i })).toBeTruthy()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Page should still be functional
|
|
147
|
+
await expect(page).toHaveURL(/team/)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('API 500 errors show appropriate error message', async ({ page }) => {
|
|
151
|
+
// Navigate to dashboard first
|
|
152
|
+
await page.goto('/dashboard')
|
|
153
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
154
|
+
|
|
155
|
+
// Intercept API to return 500
|
|
156
|
+
await page.route('**/api/**', (route) => {
|
|
157
|
+
if (route.request().url().includes('/api/audits') ||
|
|
158
|
+
route.request().url().includes('/api/users') ||
|
|
159
|
+
route.request().url().includes('/api/storage')) {
|
|
160
|
+
route.fulfill({
|
|
161
|
+
status: 500,
|
|
162
|
+
contentType: 'application/json',
|
|
163
|
+
body: JSON.stringify({
|
|
164
|
+
error: {
|
|
165
|
+
message: 'Internal Server Error',
|
|
166
|
+
code: 'INTERNAL_ERROR',
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
})
|
|
170
|
+
} else {
|
|
171
|
+
route.continue()
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Make an API request that will fail
|
|
176
|
+
const response = await page.request.get('/api/audits', {
|
|
177
|
+
failOnStatusCode: false,
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
// Verify error response format
|
|
181
|
+
expect(response.status()).toBe(500)
|
|
182
|
+
const body = await response.json()
|
|
183
|
+
expect(body).toHaveProperty('error')
|
|
184
|
+
expect(body.error).toHaveProperty('message')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('timeout errors show timeout message', async ({ page }) => {
|
|
188
|
+
test.setTimeout(20000) // Extend timeout for this test
|
|
189
|
+
|
|
190
|
+
// Navigate to dashboard first
|
|
191
|
+
await page.goto('/dashboard')
|
|
192
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
193
|
+
|
|
194
|
+
// Intercept API to simulate very slow response
|
|
195
|
+
await page.route('**/api/users**', async (route) => {
|
|
196
|
+
// Delay for 10 seconds to trigger timeout behavior
|
|
197
|
+
await new Promise((resolve) => setTimeout(resolve, 10000))
|
|
198
|
+
route.continue()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Navigate to team page - should handle slow/timeout gracefully
|
|
202
|
+
await page.goto('/team', { timeout: 15000 })
|
|
203
|
+
|
|
204
|
+
// Page should either show loading state or timeout message
|
|
205
|
+
// or gracefully handle the delay
|
|
206
|
+
await expect(page).toHaveURL(/team/)
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test.describe('Error Recovery Flow', () => {
|
|
211
|
+
test.beforeEach(async ({ page }) => {
|
|
212
|
+
const authenticated = await isAuthenticated(page)
|
|
213
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('can navigate away from error state', async ({ page }) => {
|
|
217
|
+
// Navigate to dashboard
|
|
218
|
+
await page.goto('/dashboard')
|
|
219
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
220
|
+
|
|
221
|
+
// Intercept team API to fail
|
|
222
|
+
await page.route('**/api/users**', (route) => {
|
|
223
|
+
route.fulfill({
|
|
224
|
+
status: 500,
|
|
225
|
+
contentType: 'application/json',
|
|
226
|
+
body: JSON.stringify({ error: { message: 'Server Error' } }),
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// Navigate to team page (may show error)
|
|
231
|
+
await page.goto('/team')
|
|
232
|
+
|
|
233
|
+
// Navigate to settings (should work regardless of team page error)
|
|
234
|
+
await page.goto('/settings')
|
|
235
|
+
await expect(page.getByRole('heading', { name: /settings/i })).toBeVisible()
|
|
236
|
+
|
|
237
|
+
// Navigate to dashboard (should also work)
|
|
238
|
+
await page.goto('/dashboard')
|
|
239
|
+
await expect(page).toHaveURL(/dashboard/)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
test('error state does not persist after navigation', async ({ page }) => {
|
|
243
|
+
// Navigate to dashboard
|
|
244
|
+
await page.goto('/dashboard')
|
|
245
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
246
|
+
|
|
247
|
+
// Intercept team API to fail once
|
|
248
|
+
let failCount = 0
|
|
249
|
+
await page.route('**/api/users**', (route) => {
|
|
250
|
+
failCount++
|
|
251
|
+
if (failCount <= 1) {
|
|
252
|
+
route.fulfill({
|
|
253
|
+
status: 500,
|
|
254
|
+
contentType: 'application/json',
|
|
255
|
+
body: JSON.stringify({ error: { message: 'Server Error' } }),
|
|
256
|
+
})
|
|
257
|
+
} else {
|
|
258
|
+
route.continue()
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
// Navigate to team page (may trigger error)
|
|
263
|
+
await page.goto('/team')
|
|
264
|
+
|
|
265
|
+
// Navigate to settings
|
|
266
|
+
await page.goto('/settings')
|
|
267
|
+
await expect(page.getByRole('heading', { name: /settings/i })).toBeVisible()
|
|
268
|
+
|
|
269
|
+
// Navigate back to team - should work now
|
|
270
|
+
await page.goto('/team')
|
|
271
|
+
await expect(page).toHaveURL(/team/)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
test('"Go home" button navigates to dashboard', async ({ page }) => {
|
|
275
|
+
// Navigate to dashboard
|
|
276
|
+
await page.goto('/dashboard')
|
|
277
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
278
|
+
|
|
279
|
+
// Intercept to cause error
|
|
280
|
+
await page.route('**/api/users**', (route) => {
|
|
281
|
+
route.abort('failed')
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
// Navigate to team page
|
|
285
|
+
await page.goto('/team')
|
|
286
|
+
|
|
287
|
+
// Look for Go home button if error UI is shown
|
|
288
|
+
const goHomeButton = page.getByRole('button', { name: /go home|home/i })
|
|
289
|
+
const hasButton = await goHomeButton.isVisible({ timeout: 2000 }).catch(() => false)
|
|
290
|
+
|
|
291
|
+
if (hasButton) {
|
|
292
|
+
await goHomeButton.click()
|
|
293
|
+
await expect(page).toHaveURL(/dashboard/)
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
test('"Go back" button navigates to previous page', async ({ page }) => {
|
|
298
|
+
// Navigate to settings first
|
|
299
|
+
await page.goto('/settings')
|
|
300
|
+
await expect(page.getByRole('heading', { name: /settings/i })).toBeVisible()
|
|
301
|
+
|
|
302
|
+
// Intercept to cause error
|
|
303
|
+
await page.route('**/api/users**', (route) => {
|
|
304
|
+
route.abort('failed')
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
// Navigate to team page
|
|
308
|
+
await page.goto('/team')
|
|
309
|
+
|
|
310
|
+
// Look for Go back button if error UI is shown
|
|
311
|
+
const goBackButton = page.getByRole('button', { name: /go back|back/i })
|
|
312
|
+
const hasButton = await goBackButton.isVisible({ timeout: 2000 }).catch(() => false)
|
|
313
|
+
|
|
314
|
+
if (hasButton) {
|
|
315
|
+
await goBackButton.click()
|
|
316
|
+
// Should go back to settings
|
|
317
|
+
await expect(page).toHaveURL(/settings/)
|
|
318
|
+
}
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
test.describe('Component Error Boundary', () => {
|
|
323
|
+
test.beforeEach(async ({ page }) => {
|
|
324
|
+
const authenticated = await isAuthenticated(page)
|
|
325
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
test('page remains functional after component error is recovered', async ({ page }) => {
|
|
329
|
+
// Navigate to dashboard
|
|
330
|
+
await page.goto('/dashboard')
|
|
331
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
332
|
+
|
|
333
|
+
// Force a page reload (simulates recovery from error)
|
|
334
|
+
await page.reload()
|
|
335
|
+
|
|
336
|
+
// Page should fully recover
|
|
337
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
338
|
+
await expect(page).toHaveURL(/dashboard/)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
test('sidebar navigation works after error recovery', async ({ page }) => {
|
|
342
|
+
// Navigate to dashboard
|
|
343
|
+
await page.goto('/dashboard')
|
|
344
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
345
|
+
|
|
346
|
+
// Click on team link in sidebar
|
|
347
|
+
const teamLink = page.getByRole('link', { name: /team/i }).first()
|
|
348
|
+
await teamLink.click()
|
|
349
|
+
|
|
350
|
+
// Should navigate successfully
|
|
351
|
+
await expect(page).toHaveURL(/team/)
|
|
352
|
+
|
|
353
|
+
// Navigate to settings via sidebar
|
|
354
|
+
const settingsLink = page.getByRole('link', { name: /settings/i }).first()
|
|
355
|
+
await settingsLink.click()
|
|
356
|
+
|
|
357
|
+
// Should navigate successfully
|
|
358
|
+
await expect(page).toHaveURL(/settings/)
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
test.describe('Graceful Degradation', () => {
|
|
363
|
+
test.beforeEach(async ({ page }) => {
|
|
364
|
+
const authenticated = await isAuthenticated(page)
|
|
365
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
test('app shows loading state during slow API responses', async ({ page }) => {
|
|
369
|
+
// Intercept API to add delay
|
|
370
|
+
await page.route('**/api/users**', async (route) => {
|
|
371
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
372
|
+
route.continue()
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
// Navigate to team page
|
|
376
|
+
await page.goto('/team')
|
|
377
|
+
|
|
378
|
+
// Should either show loading indicator or content
|
|
379
|
+
// (checking that something is visible)
|
|
380
|
+
await expect(page.locator('body')).toBeVisible()
|
|
381
|
+
await expect(page).toHaveURL(/team/)
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
test('app handles empty API responses gracefully', async ({ page }) => {
|
|
385
|
+
// Navigate to dashboard first
|
|
386
|
+
await page.goto('/dashboard')
|
|
387
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
388
|
+
|
|
389
|
+
// Intercept API to return empty data
|
|
390
|
+
await page.route('**/api/audits**', (route) => {
|
|
391
|
+
route.fulfill({
|
|
392
|
+
status: 200,
|
|
393
|
+
contentType: 'application/json',
|
|
394
|
+
body: JSON.stringify({ data: [], pagination: { page: 1, pageSize: 10, total: 0, totalPages: 0 } }),
|
|
395
|
+
})
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
// Make API request
|
|
399
|
+
const response = await page.request.get('/api/audits')
|
|
400
|
+
expect(response.ok()).toBeTruthy()
|
|
401
|
+
|
|
402
|
+
const body = await response.json()
|
|
403
|
+
expect(body.data).toEqual([])
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
test('app handles malformed API responses', async ({ page }) => {
|
|
407
|
+
// Navigate to dashboard first
|
|
408
|
+
await page.goto('/dashboard')
|
|
409
|
+
await expect(page.getByRole('navigation')).toBeVisible()
|
|
410
|
+
|
|
411
|
+
// Intercept API to return malformed response
|
|
412
|
+
await page.route('**/api/users**', (route) => {
|
|
413
|
+
route.fulfill({
|
|
414
|
+
status: 200,
|
|
415
|
+
contentType: 'application/json',
|
|
416
|
+
body: 'invalid json{{{',
|
|
417
|
+
})
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
// Navigate to team page
|
|
421
|
+
await page.goto('/team')
|
|
422
|
+
|
|
423
|
+
// App should handle the error gracefully
|
|
424
|
+
// Either show error UI or fallback content
|
|
425
|
+
await expect(page).toHaveURL(/team/)
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { test, expect, isAuthenticated } from '../fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* API Error Handling E2E Tests
|
|
5
|
+
*
|
|
6
|
+
* Tests API error responses and validation behavior.
|
|
7
|
+
* Form validation UI tests are in crud/team.spec.ts and crud/integrations.spec.ts.
|
|
8
|
+
*
|
|
9
|
+
* @tags @error @api
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
test.describe('API Error Handling @error @api', () => {
|
|
13
|
+
test.beforeEach(async ({ page }) => {
|
|
14
|
+
const authenticated = await isAuthenticated(page)
|
|
15
|
+
test.skip(!authenticated, 'No authenticated session available')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('API returns proper error format for invalid UUID', async ({ page }) => {
|
|
19
|
+
// Request with invalid UUID format
|
|
20
|
+
const invalidId = 'invalid-uuid-format'
|
|
21
|
+
const response = await page.request.get(`/api/users/${invalidId}`, {
|
|
22
|
+
failOnStatusCode: false,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// Should return 400 for validation error
|
|
26
|
+
expect(response.status()).toBe(400)
|
|
27
|
+
|
|
28
|
+
const body = await response.json()
|
|
29
|
+
|
|
30
|
+
// Should have error property
|
|
31
|
+
expect(body).toHaveProperty('error')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('API returns proper error format for non-existent resource', async ({ page }) => {
|
|
35
|
+
// Request a user that doesn't exist (valid UUID format)
|
|
36
|
+
const nonExistentId = '00000000-0000-0000-0000-000000000000'
|
|
37
|
+
const response = await page.request.get(`/api/users/${nonExistentId}`, {
|
|
38
|
+
failOnStatusCode: false,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// API may return 400 (bad request) or 404 (not found)
|
|
42
|
+
expect([400, 404]).toContain(response.status())
|
|
43
|
+
|
|
44
|
+
const body = await response.json()
|
|
45
|
+
expect(body).toHaveProperty('error')
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { test, expect } from '../fixtures'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error Handling E2E Tests - Unauthenticated
|
|
5
|
+
*
|
|
6
|
+
* Tests error states that don't require authentication:
|
|
7
|
+
* - 404 pages
|
|
8
|
+
* - Authentication redirect behavior
|
|
9
|
+
* - Health check endpoints
|
|
10
|
+
*
|
|
11
|
+
* @tags @error
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
test.describe('Error Handling @error', () => {
|
|
15
|
+
test.describe('404 Errors', () => {
|
|
16
|
+
test('404 page displays for unknown routes', async ({ page }) => {
|
|
17
|
+
await page.goto('/this-page-does-not-exist')
|
|
18
|
+
|
|
19
|
+
// Should show 404 content
|
|
20
|
+
await expect(page.getByText('404').first()).toBeVisible()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('404 page displays for nested unknown routes', async ({ page }) => {
|
|
24
|
+
await page.goto('/some/deeply/nested/unknown/route')
|
|
25
|
+
|
|
26
|
+
// Should show 404 content
|
|
27
|
+
await expect(page.getByText('404').first()).toBeVisible()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('404 page has navigation back to home', async ({ page }) => {
|
|
31
|
+
await page.goto('/this-page-does-not-exist')
|
|
32
|
+
|
|
33
|
+
// Should show 404 content
|
|
34
|
+
await expect(page.getByText('404').first()).toBeVisible()
|
|
35
|
+
|
|
36
|
+
// Should have some form of navigation (link, button, or nav element)
|
|
37
|
+
const homeLink = page.getByRole('link', { name: /home|back|return|go back/i })
|
|
38
|
+
const homeButton = page.getByRole('button', { name: /home|back|return|go back/i })
|
|
39
|
+
const navElement = page.getByRole('navigation')
|
|
40
|
+
|
|
41
|
+
const hasHomeLink = await homeLink.isVisible({ timeout: 3000 }).catch(() => false)
|
|
42
|
+
const hasHomeButton = await homeButton.isVisible({ timeout: 3000 }).catch(() => false)
|
|
43
|
+
const hasNavigation = await navElement.isVisible({ timeout: 3000 }).catch(() => false)
|
|
44
|
+
|
|
45
|
+
// At least one navigation option should be present
|
|
46
|
+
expect(hasHomeLink || hasHomeButton || hasNavigation).toBeTruthy()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test.describe('Authentication Errors', () => {
|
|
51
|
+
// Run serially to avoid affecting other tests' session state
|
|
52
|
+
test.describe.configure({ mode: 'serial' })
|
|
53
|
+
|
|
54
|
+
test('unauthenticated access to protected route redirects to login', async ({ page }) => {
|
|
55
|
+
// Clear any existing session
|
|
56
|
+
await page.context().clearCookies()
|
|
57
|
+
|
|
58
|
+
// Try to access protected dashboard route
|
|
59
|
+
await page.goto('/dashboard')
|
|
60
|
+
|
|
61
|
+
// Should be redirected to login
|
|
62
|
+
await expect(page).toHaveURL(/login/)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('unauthenticated access to settings redirects to login', async ({ page }) => {
|
|
66
|
+
// Clear any existing session
|
|
67
|
+
await page.context().clearCookies()
|
|
68
|
+
|
|
69
|
+
// Try to access protected settings route
|
|
70
|
+
await page.goto('/settings')
|
|
71
|
+
|
|
72
|
+
// Should be redirected to login
|
|
73
|
+
await expect(page).toHaveURL(/login/)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('unauthenticated access to team page redirects to login', async ({ page }) => {
|
|
77
|
+
// Clear any existing session
|
|
78
|
+
await page.context().clearCookies()
|
|
79
|
+
|
|
80
|
+
// Try to access protected team route
|
|
81
|
+
await page.goto('/team')
|
|
82
|
+
|
|
83
|
+
// Should be redirected to login
|
|
84
|
+
await expect(page).toHaveURL(/login/)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('invalid session cookie returns 401 from API', async ({ page }) => {
|
|
88
|
+
// Clear any existing session
|
|
89
|
+
await page.context().clearCookies()
|
|
90
|
+
|
|
91
|
+
// Try to access authenticated API endpoint
|
|
92
|
+
const response = await page.request.get('/auth/me', {
|
|
93
|
+
failOnStatusCode: false,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Should return 401 Unauthorized
|
|
97
|
+
expect(response.status()).toBe(401)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('cleared session cannot access protected API endpoints', async ({ page }) => {
|
|
101
|
+
// Clear any existing session
|
|
102
|
+
await page.context().clearCookies()
|
|
103
|
+
|
|
104
|
+
// Try to access users API endpoint
|
|
105
|
+
const response = await page.request.get('/api/users', {
|
|
106
|
+
failOnStatusCode: false,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// Should return 401 Unauthorized
|
|
110
|
+
expect(response.status()).toBe(401)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test.describe('API Health Endpoints', () => {
|
|
115
|
+
test('health endpoint returns proper format', async ({ request, baseURL }) => {
|
|
116
|
+
const response = await request.get(`${baseURL}/health`, {
|
|
117
|
+
failOnStatusCode: false,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// Check content type to ensure we're hitting the API
|
|
121
|
+
const contentType = response.headers()['content-type'] || ''
|
|
122
|
+
|
|
123
|
+
if (!contentType.includes('application/json')) {
|
|
124
|
+
test.skip(true, 'API not available in current dev server setup')
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Accept 200 (healthy) or 503 (unhealthy but responding)
|
|
129
|
+
expect([200, 503]).toContain(response.status())
|
|
130
|
+
|
|
131
|
+
const body = await response.json()
|
|
132
|
+
|
|
133
|
+
// Should have proper structure
|
|
134
|
+
expect(body).toHaveProperty('status')
|
|
135
|
+
expect(body).toHaveProperty('checks')
|
|
136
|
+
|
|
137
|
+
// Status should be a valid value
|
|
138
|
+
expect(['healthy', 'unhealthy', 'degraded']).toContain(body.status)
|
|
139
|
+
|
|
140
|
+
// Checks should be an object with service statuses
|
|
141
|
+
expect(typeof body.checks).toBe('object')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test('liveness endpoint returns proper format', async ({ request, baseURL }) => {
|
|
145
|
+
const response = await request.get(`${baseURL}/health/live`, {
|
|
146
|
+
failOnStatusCode: false,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// Check content type
|
|
150
|
+
const contentType = response.headers()['content-type'] || ''
|
|
151
|
+
|
|
152
|
+
if (!contentType.includes('application/json')) {
|
|
153
|
+
test.skip(true, 'API not available in current dev server setup')
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
expect(response.ok()).toBeTruthy()
|
|
158
|
+
|
|
159
|
+
const body = await response.json()
|
|
160
|
+
expect(body).toHaveProperty('alive')
|
|
161
|
+
expect(body.alive).toBe(true)
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
test.describe('Network Error Simulation @slow', () => {
|
|
166
|
+
test.skip('page handles slow network gracefully', async ({ page }) => {
|
|
167
|
+
// SKIPPED: This test takes 60-90s - run with: npx playwright test --grep @slow
|
|
168
|
+
test.setTimeout(90000)
|
|
169
|
+
|
|
170
|
+
// Simulate slow network (moderate throttling, not extreme)
|
|
171
|
+
const cdp = await page.context().newCDPSession(page)
|
|
172
|
+
await cdp.send('Network.emulateNetworkConditions', {
|
|
173
|
+
offline: false,
|
|
174
|
+
downloadThroughput: 200 * 1024, // 200kb/s (more realistic slow 3G)
|
|
175
|
+
uploadThroughput: 200 * 1024,
|
|
176
|
+
latency: 500, // 500ms latency
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// Navigate to home page with extended timeout
|
|
180
|
+
await page.goto('/', { timeout: 60000, waitUntil: 'domcontentloaded' })
|
|
181
|
+
|
|
182
|
+
// Page should still load (eventually)
|
|
183
|
+
await expect(page.locator('body')).toBeVisible({ timeout: 60000 })
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('page shows appropriate state when offline', async ({ page }) => {
|
|
187
|
+
// First load the page while online
|
|
188
|
+
await page.goto('/')
|
|
189
|
+
await expect(page.locator('body')).toBeVisible()
|
|
190
|
+
|
|
191
|
+
// Go offline
|
|
192
|
+
await page.context().setOffline(true)
|
|
193
|
+
|
|
194
|
+
// Try to navigate to another page
|
|
195
|
+
try {
|
|
196
|
+
await page.goto('/login', { timeout: 5000 })
|
|
197
|
+
} catch {
|
|
198
|
+
// Navigation should fail when offline - this is expected
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Go back online for cleanup
|
|
202
|
+
await page.context().setOffline(false)
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
})
|