create-ailk 0.5.0 → 0.6.0
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/README.md +55 -8
- package/component-catalog.json +336 -27
- package/component-catalog.md +25 -12
- package/dist/lib/api-boot-assertion.d.ts +46 -0
- package/dist/lib/api-boot-assertion.js +70 -0
- package/dist/lib/import-graph.d.ts +29 -0
- package/dist/lib/import-graph.js +140 -0
- package/dist/module-architecture.d.ts +1 -0
- package/dist/module-architecture.js +253 -127
- package/dist/surfaces.js +105 -7
- package/dist/template-manifest.d.ts +33 -0
- package/dist/template-manifest.js +99 -16
- package/dist/template-names.d.ts +1 -1
- package/dist/template-names.js +1 -0
- package/package.json +3 -2
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +22 -2
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +9 -2
- package/site-templates/marketing-site/ailk-template.json +1 -1
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +2 -2
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +1 -0
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +10 -2
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +47 -35
- package/site-templates/minimalism-site/ailk-template.json +2 -1
- package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
- package/site-templates/minimalism-site/content/en/HomePage/home.mdx +49 -19
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
- package/site-templates/simple-service/ailk-template.json +10 -0
- package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
- package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
- package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
- package/site-templates/simple-service/provenance.json +4 -0
- package/templates/.env.example +72 -7
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
- package/templates/apps/api/.env.example +13 -0
- package/templates/apps/api/CLAUDE.md +26 -2
- package/templates/apps/api/jest.config.cjs +3 -0
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +83 -7
- package/templates/apps/api/src/__tests__/server.test.ts +471 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +2 -0
- package/templates/apps/api/src/config/index.ts +62 -0
- package/templates/apps/api/src/config/modules.ts +7 -3
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +238 -2
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
- package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
- package/templates/apps/api/src/lib/content-adapter.ts +57 -31
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
- package/templates/apps/api/src/lib/error-serializer.ts +23 -3
- package/templates/apps/api/src/lib/outbound-http.ts +7 -2
- package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
- package/templates/apps/api/src/lib/site.ts +208 -9
- package/templates/apps/api/src/lib/stripe.ts +66 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +182 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +42 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
- package/templates/apps/api/src/openapi/spec.ts +543 -2
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
- package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
- package/templates/apps/api/src/routes/auth/user.ts +61 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
- package/templates/apps/api/src/routes/checkout/index.ts +34 -0
- package/templates/apps/api/src/routes/checkout/session.ts +139 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
- package/templates/apps/api/src/routes/content/get-page.ts +94 -12
- package/templates/apps/api/src/routes/content/index.ts +13 -73
- package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
- package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
- package/templates/apps/api/src/routes/content/search.ts +129 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
- package/templates/apps/api/src/routes/leads/index.ts +100 -13
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
- package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
- package/templates/apps/api/src/routes/requests/index.ts +54 -0
- package/templates/apps/api/src/routes/webhooks/README.md +265 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
- package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
- package/templates/apps/api/src/server.ts +439 -49
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
- package/templates/apps/api/src/services/abandon-email.ts +8 -2
- package/templates/apps/api/src/services/aeo-score.ts +49 -12
- package/templates/apps/api/src/services/audit-log.ts +668 -0
- package/templates/apps/api/src/services/consent.ts +18 -0
- package/templates/apps/api/src/services/default-tenant.ts +70 -31
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
- package/templates/apps/api/src/services/dunning-policy.ts +294 -0
- package/templates/apps/api/src/services/entitlements.ts +80 -0
- package/templates/apps/api/src/services/lead-promotion.ts +13 -14
- package/templates/apps/api/src/services/lead-routing.ts +161 -2
- package/templates/apps/api/src/services/leads-inbox.ts +23 -2
- package/templates/apps/api/src/services/listing-config.ts +27 -9
- package/templates/apps/api/src/services/project-listings.ts +201 -6
- package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
- package/templates/apps/api/src/services/request-confirm.ts +646 -0
- package/templates/apps/api/src/services/revalidation.ts +173 -0
- package/templates/apps/api/src/services/verified-email.ts +71 -0
- package/templates/apps/api/src/vercel-handler.ts +75 -35
- package/templates/apps/mcp/.env.example +17 -0
- package/templates/apps/mcp/CLAUDE.md +57 -24
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
- package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
- package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
- package/templates/apps/mcp/__tests__/index.test.ts +39 -0
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
- package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
- package/templates/apps/mcp/__tests__/parity.test.ts +10 -7
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
- package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
- package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
- package/templates/apps/mcp/__tests__/server.test.ts +67 -14
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
- package/templates/apps/mcp/jest.config.cjs +13 -0
- package/templates/apps/mcp/package.json +2 -0
- package/templates/apps/mcp/src/abuse/index.ts +159 -24
- package/templates/apps/mcp/src/config/modules.ts +10 -5
- package/templates/apps/mcp/src/http-transport.ts +102 -29
- package/templates/apps/mcp/src/index.ts +73 -11
- package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
- package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
- package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
- package/templates/apps/mcp/src/lib/prisma.ts +39 -0
- package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
- package/templates/apps/mcp/src/server.ts +59 -35
- package/templates/apps/mcp/src/tools/audit.ts +26 -2
- package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
- package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
- package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
- package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
- package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
- package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
- package/templates/apps/mcp/src/tools/index.ts +6 -4
- package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
- package/templates/apps/mcp/src/tools/purchase.ts +128 -45
- package/templates/apps/mcp/src/tools/read_content.ts +20 -6
- package/templates/apps/mcp/src/tools/search_content.ts +11 -4
- package/templates/apps/web/.env.example +71 -5
- package/templates/apps/web/CLAUDE.md +44 -0
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
- package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
- package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
- package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +63 -8
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +18 -1
- package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/layout.tsx +133 -87
- package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
- package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
- package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
- package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
- package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
- package/templates/apps/web/app/api/revalidate/route.ts +194 -0
- package/templates/apps/web/auth.ts +407 -8
- package/templates/apps/web/jest.config.cjs +8 -4
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +18 -1
- package/templates/apps/web/lib/auth-email.ts +318 -0
- package/templates/apps/web/lib/auth-env.ts +85 -0
- package/templates/apps/web/lib/auth-routes.ts +55 -0
- package/templates/apps/web/lib/meeting-slugs.ts +53 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +5 -1
- package/templates/apps/web/messages/en.json +187 -0
- package/templates/apps/web/package.json +2 -1
- package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
- package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
- package/templates/database/CHANGELOG.md +142 -0
- package/templates/database/README.md +10 -3
- package/templates/database/__tests__/appointment.db.test.ts +106 -0
- package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
- package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
- package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
- package/templates/database/__tests__/tenancy.test.ts +141 -0
- package/templates/database/config/schema.prisma +79 -2
- package/templates/database/content/schema.prisma +87 -11
- package/templates/database/inbox/schema.prisma +452 -9
- package/templates/database/jest.config.cjs +5 -3
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
- package/templates/database/package.json +1 -1
- package/templates/database/tenancy.ts +86 -1
- package/templates/database/tsconfig.json +1 -0
- package/versions.json +34 -34
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
- package/templates/apps/api/src/routes/content/create.ts +0 -115
- package/templates/apps/api/src/routes/content/delete.ts +0 -82
- package/templates/apps/api/src/routes/content/update.ts +0 -112
- package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
- package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
- package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
- package/templates/apps/mcp/src/tools/create_page.ts +0 -96
- package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
- package/templates/apps/mcp/src/tools/update_page.ts +0 -92
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the `(auth)` route group's five pages and its layout (#5963).
|
|
3
|
+
*
|
|
4
|
+
* This suite is AC-1's and AC-3's scorer:
|
|
5
|
+
* - AC-1: each of the five pages renders a real card (no 404 path), and the
|
|
6
|
+
* group's layout mounts AuthShell with no Nav and no marketing Footer.
|
|
7
|
+
* - AC-2 (the half jsdom CAN see): the moved sign-in card carries no
|
|
8
|
+
* `min-h-screen` / `items-center` / `justify-center` framing of its own —
|
|
9
|
+
* a regression to vertical centering fails a unit test, not just a frame
|
|
10
|
+
* review — and the shell's column carries the 400px cap.
|
|
11
|
+
* - AC-3: `/sign-up` follows `isSignUpOpen()`, and `/forgot-password` +
|
|
12
|
+
* `/reset-password` follow the reset predicate, in both directions. (#5962
|
|
13
|
+
* repointed that predicate from `isResetAvailable()` to
|
|
14
|
+
* `resetSendConfigured()`; the mock below binds both to one fn, so these
|
|
15
|
+
* cases read identically either way.)
|
|
16
|
+
*
|
|
17
|
+
* `next-intl/server` is mocked globally (apps/web/__tests__/setup.ts) against
|
|
18
|
+
* the real en.json, so the `auth` copy is asserted by its real strings.
|
|
19
|
+
*/
|
|
20
|
+
import { render, screen } from "@testing-library/react";
|
|
21
|
+
|
|
22
|
+
const mockGetActiveProviderIds = jest.fn();
|
|
23
|
+
const mockIsSignUpOpen = jest.fn();
|
|
24
|
+
const mockIsResetAvailable = jest.fn();
|
|
25
|
+
// eslint-disable-next-line no-restricted-syntax -- #507: mock-internal rewrite pending; auth-env.ts is a pure env-introspection module with no external boundary to mock instead.
|
|
26
|
+
jest.mock("../../../../lib/auth-env", () => ({
|
|
27
|
+
__esModule: true,
|
|
28
|
+
getActiveProviderIds: () => mockGetActiveProviderIds(),
|
|
29
|
+
isSignUpOpen: () => mockIsSignUpOpen(),
|
|
30
|
+
isResetAvailable: () => mockIsResetAvailable(),
|
|
31
|
+
// #5962 repointed both reset pages from the flag-only `isResetAvailable()`
|
|
32
|
+
// at the send conjunction. This is a PARTIAL module mock, so an absent
|
|
33
|
+
// member is `undefined` rather than the real function — the two pages would
|
|
34
|
+
// throw rather than take a branch. Bound to the SAME jest.fn() the flag
|
|
35
|
+
// already drives, so every case below keeps the exact truth value and the
|
|
36
|
+
// exact expectation it was written with: no assertion in this file changes.
|
|
37
|
+
resetSendConfigured: () => mockIsResetAvailable(),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
jest.mock("better-auth/react", () => ({
|
|
41
|
+
__esModule: true,
|
|
42
|
+
createAuthClient: () => ({
|
|
43
|
+
signIn: { social: jest.fn(), email: jest.fn() },
|
|
44
|
+
signUp: { email: jest.fn() },
|
|
45
|
+
}),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
// `readLocales()` reads project.yaml relative to cwd, which jest does not
|
|
49
|
+
// provide from apps/web — the sibling layout suite stubs it for the same
|
|
50
|
+
// reason. The locale list only feeds the language switcher, stubbed below.
|
|
51
|
+
jest.mock("@working-theory/web-i18n", () => ({
|
|
52
|
+
readLocales: jest.fn().mockReturnValue(["en"]),
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
jest.mock("next/headers", () => ({
|
|
56
|
+
headers: jest.fn().mockResolvedValue({ get: () => "/sign-in" }),
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
// The language switcher is an island with its own router side-effects; the
|
|
60
|
+
// layout suite next door stubs it for the same reason.
|
|
61
|
+
jest.mock("@working-theory/ui/blocks", () => {
|
|
62
|
+
const actual = jest.requireActual("@working-theory/ui/blocks");
|
|
63
|
+
return {
|
|
64
|
+
...actual,
|
|
65
|
+
LocaleSwitcherNav: ({ current }: { current: string }) => (
|
|
66
|
+
<span data-testid="language-switcher-stub">{current}</span>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
import en from "../../../../messages/en.json";
|
|
72
|
+
import AuthLayout from "../layout";
|
|
73
|
+
import ForgotPasswordPage from "../forgot-password/page";
|
|
74
|
+
import ResetPasswordPage from "../reset-password/page";
|
|
75
|
+
import SignInPage from "../sign-in/page";
|
|
76
|
+
import SignUpPage from "../sign-up/page";
|
|
77
|
+
import VerifyEmailPage from "../verify-email/page";
|
|
78
|
+
|
|
79
|
+
const params = Promise.resolve({ locale: "en" });
|
|
80
|
+
|
|
81
|
+
async function renderPage(
|
|
82
|
+
page: (props: {
|
|
83
|
+
params: Promise<{ locale: string }>;
|
|
84
|
+
searchParams: Promise<Record<string, string>>;
|
|
85
|
+
}) => Promise<React.ReactElement>,
|
|
86
|
+
): Promise<void> {
|
|
87
|
+
render(
|
|
88
|
+
await page({
|
|
89
|
+
params: Promise.resolve({ locale: "en" }),
|
|
90
|
+
searchParams: Promise.resolve({}),
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
mockGetActiveProviderIds.mockReset().mockReturnValue(["google"]);
|
|
97
|
+
mockIsSignUpOpen.mockReset().mockReturnValue(false);
|
|
98
|
+
mockIsResetAvailable.mockReset().mockReturnValue(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ── AC-1: the five routes resolve ─────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
describe("the five (auth) routes resolve", () => {
|
|
104
|
+
it.each([
|
|
105
|
+
["/sign-in", SignInPage, "sign-in-page"],
|
|
106
|
+
["/sign-up", SignUpPage, "sign-up-page"],
|
|
107
|
+
["/forgot-password", ForgotPasswordPage, "forgot-password-page"],
|
|
108
|
+
["/reset-password", ResetPasswordPage, "reset-password-page"],
|
|
109
|
+
["/verify-email", VerifyEmailPage, "verify-email-page"],
|
|
110
|
+
] as const)("%s renders a card", async (_path, Page, testid) => {
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- the five pages differ in props (only sign-in takes searchParams); the shared runner passes both.
|
|
112
|
+
await renderPage(Page as any);
|
|
113
|
+
expect(screen.getByTestId(testid)).toBeInTheDocument();
|
|
114
|
+
expect(screen.getByTestId("auth-card")).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ── AC-1: the group's layout is AuthShell, with no marketing chrome ──────────
|
|
119
|
+
|
|
120
|
+
describe("the (auth) layout", () => {
|
|
121
|
+
async function renderLayout(): Promise<void> {
|
|
122
|
+
render(
|
|
123
|
+
await AuthLayout({
|
|
124
|
+
children: <span data-testid="auth-child">page</span>,
|
|
125
|
+
params,
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
it("mounts AuthShell", async () => {
|
|
131
|
+
await renderLayout();
|
|
132
|
+
expect(screen.getByTestId("auth-shell")).toBeInTheDocument();
|
|
133
|
+
expect(screen.getByTestId("auth-child")).toBeInTheDocument();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("renders no Nav and no marketing Footer", async () => {
|
|
137
|
+
await renderLayout();
|
|
138
|
+
expect(
|
|
139
|
+
document.querySelector('nav[aria-label="Primary"]'),
|
|
140
|
+
).not.toBeInTheDocument();
|
|
141
|
+
expect(document.querySelector(".ailk-footer")).not.toBeInTheDocument();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("renders the brand mark and the slim footer's three links", async () => {
|
|
145
|
+
await renderLayout();
|
|
146
|
+
expect(screen.getByTestId("auth-shell-brand")).toBeInTheDocument();
|
|
147
|
+
const footer = screen.getByTestId("auth-shell-footer");
|
|
148
|
+
expect(footer.querySelectorAll("a")).toHaveLength(3);
|
|
149
|
+
expect(
|
|
150
|
+
screen.getByRole("link", { name: en.auth.footerPrivacy }),
|
|
151
|
+
).toHaveAttribute("href", "/privacy");
|
|
152
|
+
expect(
|
|
153
|
+
screen.getByRole("link", { name: en.auth.footerHelp }),
|
|
154
|
+
).toHaveAttribute("href", "/faq");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("owns exactly one <main> landmark (ADR-0027)", async () => {
|
|
158
|
+
await renderLayout();
|
|
159
|
+
expect(document.querySelectorAll("main")).toHaveLength(1);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("caps the content column at 400px — AC-2's class contract", async () => {
|
|
163
|
+
await renderLayout();
|
|
164
|
+
expect(screen.getByTestId("auth-shell-column")).toHaveClass("max-w-100");
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// ── AC-2: the moved sign-in card no longer frames the page ───────────────────
|
|
169
|
+
|
|
170
|
+
describe("the moved sign-in card", () => {
|
|
171
|
+
it("carries no min-h-screen / items-center / justify-center framing", async () => {
|
|
172
|
+
await renderPage(SignInPage);
|
|
173
|
+
const wrapper = screen.getByTestId("sign-in-page");
|
|
174
|
+
expect(wrapper).not.toHaveClass("min-h-screen");
|
|
175
|
+
expect(wrapper).not.toHaveClass("items-center");
|
|
176
|
+
expect(wrapper).not.toHaveClass("justify-center");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("points the forgot link at /forgot-password when reset is available (it was a dead '#')", async () => {
|
|
180
|
+
mockGetActiveProviderIds.mockReturnValue(["google", "email"]);
|
|
181
|
+
mockIsResetAvailable.mockReturnValue(true);
|
|
182
|
+
await renderPage(SignInPage);
|
|
183
|
+
expect(screen.getByTestId("auth-forgot")).toHaveAttribute(
|
|
184
|
+
"href",
|
|
185
|
+
"/forgot-password",
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("omits the forgot link when password reset is unavailable (#6018 AC-1)", async () => {
|
|
190
|
+
mockGetActiveProviderIds.mockReturnValue(["google", "email"]);
|
|
191
|
+
mockIsResetAvailable.mockReturnValue(false);
|
|
192
|
+
await renderPage(SignInPage);
|
|
193
|
+
expect(screen.queryByTestId("auth-forgot")).not.toBeInTheDocument();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("renders no logo inside the card — the mark is the shell's", async () => {
|
|
197
|
+
await renderPage(SignInPage);
|
|
198
|
+
expect(screen.queryByTestId("auth-card-logo")).not.toBeInTheDocument();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ── AC-3: the two env predicates drive the pages ─────────────────────────────
|
|
203
|
+
|
|
204
|
+
describe("/sign-up follows isSignUpOpen()", () => {
|
|
205
|
+
it("renders the open form when sign-up is open", async () => {
|
|
206
|
+
mockIsSignUpOpen.mockReturnValue(true);
|
|
207
|
+
mockGetActiveProviderIds.mockReturnValue(["google", "email"]);
|
|
208
|
+
await renderPage(SignUpPage);
|
|
209
|
+
expect(screen.getByTestId("auth-submit")).toBeInTheDocument();
|
|
210
|
+
expect(
|
|
211
|
+
screen.getByRole("heading", { name: en.auth.signUpHeading }),
|
|
212
|
+
).toBeInTheDocument();
|
|
213
|
+
expect(screen.queryByTestId("auth-invitation-body")).not.toBeInTheDocument();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("renders the invitation intent when sign-up is closed", async () => {
|
|
217
|
+
mockIsSignUpOpen.mockReturnValue(false);
|
|
218
|
+
await renderPage(SignUpPage);
|
|
219
|
+
expect(screen.getByTestId("auth-invitation-body")).toBeInTheDocument();
|
|
220
|
+
expect(
|
|
221
|
+
screen.getByRole("heading", { name: en.auth.invitationHeading }),
|
|
222
|
+
).toBeInTheDocument();
|
|
223
|
+
expect(screen.queryByTestId("auth-submit")).not.toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe("/forgot-password and /reset-password follow the reset predicate", () => {
|
|
228
|
+
it("renders the forgot intent when reset is available", async () => {
|
|
229
|
+
mockIsResetAvailable.mockReturnValue(true);
|
|
230
|
+
await renderPage(ForgotPasswordPage);
|
|
231
|
+
expect(screen.getByTestId("auth-email")).toBeInTheDocument();
|
|
232
|
+
expect(
|
|
233
|
+
screen.getByRole("heading", { name: en.auth.forgotHeading }),
|
|
234
|
+
).toBeInTheDocument();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("renders the not-available state when reset is unavailable", async () => {
|
|
238
|
+
mockIsResetAvailable.mockReturnValue(false);
|
|
239
|
+
await renderPage(ForgotPasswordPage);
|
|
240
|
+
expect(screen.getByTestId("auth-invitation-body")).toBeInTheDocument();
|
|
241
|
+
expect(
|
|
242
|
+
screen.getByRole("heading", { name: en.auth.forgotUnavailableHeading }),
|
|
243
|
+
).toBeInTheDocument();
|
|
244
|
+
expect(screen.queryByTestId("auth-email")).not.toBeInTheDocument();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("renders the reset intent when reset is available", async () => {
|
|
248
|
+
mockIsResetAvailable.mockReturnValue(true);
|
|
249
|
+
await renderPage(ResetPasswordPage);
|
|
250
|
+
expect(screen.getByTestId("auth-password")).toBeInTheDocument();
|
|
251
|
+
expect(
|
|
252
|
+
screen.getByRole("heading", { name: en.auth.resetHeading }),
|
|
253
|
+
).toBeInTheDocument();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("renders its own not-available copy when reset is unavailable", async () => {
|
|
257
|
+
mockIsResetAvailable.mockReturnValue(false);
|
|
258
|
+
await renderPage(ResetPasswordPage);
|
|
259
|
+
expect(screen.getByTestId("auth-invitation-body")).toHaveTextContent(
|
|
260
|
+
en.auth.resetUnavailableBody,
|
|
261
|
+
);
|
|
262
|
+
expect(screen.queryByTestId("auth-password")).not.toBeInTheDocument();
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// ── Every non-form page routes back to sign-in ───────────────────────────────
|
|
267
|
+
|
|
268
|
+
describe("the copy-only intents route back to sign-in", () => {
|
|
269
|
+
it.each([
|
|
270
|
+
["sign-up (closed)", SignUpPage],
|
|
271
|
+
["forgot-password (unavailable)", ForgotPasswordPage],
|
|
272
|
+
["reset-password (unavailable)", ResetPasswordPage],
|
|
273
|
+
["verify-email", VerifyEmailPage],
|
|
274
|
+
] as const)("%s carries the backToSignIn link", async (_name, Page) => {
|
|
275
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- see the note on the five-routes runner above.
|
|
276
|
+
await renderPage(Page as any);
|
|
277
|
+
expect(screen.getByTestId("auth-footer-link")).toHaveAttribute(
|
|
278
|
+
"href",
|
|
279
|
+
"/sign-in",
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
});
|