@nextsparkjs/theme-default 0.1.0-beta.17 → 0.1.0-beta.170
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/api/ai/chat/stream/route.ts +4 -1
- package/api/ai/orchestrator/route.ts +10 -3
- package/api/ai/single-agent/route.ts +10 -3
- package/api/ai/usage/route.ts +4 -1
- package/blocks/benefits/component.tsx +4 -4
- package/blocks/cta-section/component.tsx +4 -4
- package/blocks/faq-accordion/component.tsx +2 -2
- package/blocks/features-grid/component.tsx +5 -5
- package/blocks/hero/component.tsx +2 -2
- package/blocks/hero/fields.ts +1 -1
- package/blocks/hero-with-form/component.tsx +7 -7
- package/blocks/hero-with-form/fields.ts +1 -1
- package/blocks/jumbotron/component.tsx +7 -7
- package/blocks/jumbotron/fields.ts +1 -1
- package/blocks/logo-cloud/component.tsx +6 -6
- package/blocks/logo-cloud/fields.ts +1 -1
- package/blocks/post-content/component.tsx +2 -2
- package/blocks/pricing-table/component.tsx +5 -5
- package/blocks/split-content/component.tsx +5 -5
- package/blocks/split-content/fields.ts +1 -1
- package/blocks/stats-counter/component.tsx +9 -9
- package/blocks/testimonials/component.tsx +4 -4
- package/blocks/testimonials/fields.ts +1 -1
- package/blocks/text-content/component.tsx +12 -10
- package/blocks/timeline/component.tsx +12 -12
- package/blocks/video-hero/component.tsx +7 -7
- package/blocks/video-hero/fields.ts +1 -1
- package/components/ai-chat/ChatPanel.tsx +7 -7
- package/components/ai-chat/Message.tsx +2 -2
- package/components/ai-chat/MessageInput.tsx +3 -3
- package/components/ai-chat/MessageList.tsx +3 -3
- package/components/ai-chat/TypingIndicator.tsx +2 -2
- package/config/app.config.ts +90 -62
- package/config/billing.config.ts +5 -10
- package/config/dashboard.config.ts +14 -0
- package/config/features.config.ts +10 -0
- package/config/permissions.config.ts +22 -23
- package/docs/{01-overview → public/01-overview}/01-introduction.md +5 -0
- package/docs/{01-overview → public/01-overview}/02-customization.md +5 -0
- package/docs/{02-features → public/02-features}/03-tasks-entity.md +5 -0
- package/docs/{03-ai → public/03-ai}/01-overview.md +5 -0
- package/docs/{03-ai → public/03-ai}/02-customization.md +5 -0
- package/docs/superadmin/01-setup/01-configuration.md +79 -0
- package/docs/superadmin/01-setup/02-deployment.md +82 -0
- package/docs/superadmin/02-management/01-users.md +83 -0
- package/docs/superadmin/03-integrations/01-langchain.md +139 -0
- package/emails/verify-email.ts +33 -0
- package/entities/customers/api/docs.md +107 -0
- package/entities/customers/api/presets.ts +80 -0
- package/entities/pages/api/docs.md +114 -0
- package/entities/pages/api/presets.ts +72 -0
- package/entities/posts/api/docs.md +120 -0
- package/entities/posts/api/presets.ts +74 -0
- package/entities/tasks/api/docs.md +126 -0
- package/entities/tasks/api/presets.ts +84 -0
- package/lib/selectors.ts +7 -4
- package/messages/de/admin.json +45 -0
- package/messages/en/admin.json +56 -0
- package/messages/en/navigation.json +2 -1
- package/messages/es/admin.json +56 -0
- package/messages/es/navigation.json +2 -1
- package/messages/fr/admin.json +45 -0
- package/messages/it/admin.json +45 -0
- package/messages/pt/admin.json +45 -0
- package/migrations/089_add_editor_team_role.sql +15 -23
- package/migrations/090_demo_users_teams.sql +11 -11
- package/migrations/091_greek_teams_billing.sql +15 -15
- package/migrations/093_pages_sample_data.sql +7 -7
- package/migrations/098_patterns_sample_data.sql +234 -0
- package/nextsparkjs-theme-default-0.1.0-beta.137.tgz +0 -0
- package/package.json +4 -3
- package/styles/globals.css +42 -0
- package/templates/(public)/page.tsx +1 -1
- package/tests/cypress/e2e/_utils/devtools/access.bdd.md +262 -0
- package/tests/cypress/e2e/_utils/devtools/access.cy.ts +171 -0
- package/tests/cypress/e2e/_utils/devtools/navigation.bdd.md +261 -0
- package/tests/cypress/e2e/_utils/devtools/navigation.cy.ts +157 -0
- package/tests/cypress/e2e/_utils/devtools/pages.bdd.md +303 -0
- package/tests/cypress/e2e/_utils/devtools/pages.cy.ts +184 -0
- package/tests/cypress/e2e/_utils/docs/README.md +215 -0
- package/tests/cypress/e2e/_utils/selectors/auth.bdd.md +354 -0
- package/tests/cypress/e2e/_utils/selectors/auth.cy.ts +310 -0
- package/tests/cypress/e2e/_utils/selectors/billing.bdd.md +276 -0
- package/tests/cypress/e2e/_utils/selectors/billing.cy.ts +182 -0
- package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +615 -0
- package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +783 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.bdd.md +205 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +137 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.bdd.md +147 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +114 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +76 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +68 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +326 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +177 -0
- package/tests/cypress/e2e/_utils/selectors/devtools.bdd.md +306 -0
- package/tests/cypress/e2e/_utils/selectors/devtools.cy.ts +273 -0
- package/tests/cypress/e2e/_utils/selectors/global-search.bdd.md +115 -0
- package/tests/cypress/e2e/_utils/selectors/global-search.cy.ts +93 -0
- package/tests/cypress/e2e/_utils/selectors/patterns.bdd.md +388 -0
- package/tests/cypress/e2e/_utils/selectors/patterns.cy.ts +559 -0
- package/tests/cypress/e2e/_utils/selectors/public.cy.ts +112 -0
- package/tests/cypress/e2e/_utils/selectors/settings-api-keys.bdd.md +266 -0
- package/tests/cypress/e2e/_utils/selectors/settings-api-keys.cy.ts +233 -0
- package/tests/cypress/e2e/_utils/selectors/settings-billing.bdd.md +78 -0
- package/tests/cypress/e2e/_utils/selectors/settings-billing.cy.ts +108 -0
- package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +129 -0
- package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +115 -0
- package/tests/cypress/e2e/_utils/selectors/settings-password.bdd.md +82 -0
- package/tests/cypress/e2e/_utils/selectors/settings-password.cy.ts +74 -0
- package/tests/cypress/e2e/_utils/selectors/settings-profile.bdd.md +77 -0
- package/tests/cypress/e2e/_utils/selectors/settings-profile.cy.ts +79 -0
- package/tests/cypress/e2e/_utils/selectors/settings-teams.bdd.md +130 -0
- package/tests/cypress/e2e/_utils/selectors/settings-teams.cy.ts +86 -0
- package/tests/cypress/e2e/_utils/selectors/superadmin.bdd.md +261 -0
- package/tests/cypress/e2e/_utils/selectors/superadmin.cy.ts +193 -0
- package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +593 -0
- package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +864 -0
- package/tests/cypress/e2e/_utils/selectors/taxonomies.cy.ts +126 -0
- package/tests/cypress/e2e/_utils/selectors/teams.bdd.md +278 -0
- package/tests/cypress/e2e/_utils/selectors/teams.cy.ts +195 -0
- package/tests/cypress/e2e/_utils/superadmin/all-teams.bdd.md +261 -0
- package/tests/cypress/e2e/_utils/superadmin/all-teams.cy.ts +177 -0
- package/tests/cypress/e2e/_utils/superadmin/all-users.bdd.md +406 -0
- package/tests/cypress/e2e/_utils/superadmin/all-users.cy.ts +294 -0
- package/tests/cypress/e2e/_utils/superadmin/dashboard.bdd.md +235 -0
- package/tests/cypress/e2e/_utils/superadmin/dashboard.cy.ts +149 -0
- package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.bdd.md +290 -0
- package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.cy.ts +194 -0
- package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
- package/tests/cypress/e2e/ai/chat-api.cy.ts +119 -0
- package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
- package/tests/cypress/e2e/api/_core/billing/BillingAPIController.js +319 -0
- package/tests/cypress/e2e/api/_core/billing/check-action.cy.ts +326 -0
- package/tests/cypress/e2e/api/_core/billing/checkout.cy.ts +358 -0
- package/tests/cypress/e2e/api/_core/billing/lifecycle.cy.ts +423 -0
- package/tests/cypress/e2e/api/_core/billing/plans/README.md +345 -0
- package/tests/cypress/e2e/api/_core/billing/plans/business.cy.ts +412 -0
- package/tests/cypress/e2e/api/_core/billing/plans/downgrade.cy.ts +510 -0
- package/tests/cypress/e2e/api/_core/billing/plans/fixtures/billing-plans.json +163 -0
- package/tests/cypress/e2e/api/_core/billing/plans/free.cy.ts +500 -0
- package/tests/cypress/e2e/api/_core/billing/plans/pro.cy.ts +497 -0
- package/tests/cypress/e2e/api/_core/billing/plans/starter.cy.ts +342 -0
- package/tests/cypress/e2e/api/_core/billing/portal.cy.ts +313 -0
- package/tests/cypress/e2e/api/_core/devtools/registries.bdd.md +300 -0
- package/tests/cypress/e2e/api/_core/devtools/registries.cy.ts +368 -0
- package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.bdd.md +375 -0
- package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.cy.ts +346 -0
- package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.bdd.md +451 -0
- package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.cy.ts +447 -0
- package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.bdd.md +649 -0
- package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.cy.ts +333 -0
- package/tests/cypress/e2e/api/_core/security/security-headers.cy.ts +601 -0
- package/tests/cypress/e2e/api/_core/settings/api-keys.crud.cy.ts +923 -0
- package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
- package/tests/cypress/e2e/api/_core/users/users-crud.cy.ts +469 -0
- package/tests/cypress/e2e/api/_core/users/users-metas.cy.ts +913 -0
- package/tests/cypress/e2e/api/_core/users/users-security.cy.ts +375 -0
- package/tests/cypress/e2e/api/entities/customers/customers-crud.cy.ts +648 -0
- package/tests/cypress/e2e/api/entities/customers/customers-metas.cy.ts +839 -0
- package/tests/cypress/e2e/api/entities/media/media-crud.cy.ts +600 -0
- package/tests/cypress/e2e/api/entities/media/media-role-permissions.cy.ts +617 -0
- package/tests/cypress/e2e/api/entities/media/media-team-isolation.cy.ts +464 -0
- package/tests/cypress/e2e/api/entities/pages/blocks-scope.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/pages/pages-crud.cy.ts +425 -0
- package/tests/cypress/e2e/api/entities/pages/pages-status.cy.ts +335 -0
- package/tests/cypress/e2e/api/entities/posts/post-categories-crud.cy.ts +610 -0
- package/tests/cypress/e2e/api/entities/posts/posts-crud.cy.ts +709 -0
- package/tests/cypress/e2e/api/entities/posts/posts-status.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/tasks/tasks-crud.cy.ts +602 -0
- package/tests/cypress/e2e/api/entities/tasks/tasks-metas.cy.ts +878 -0
- package/tests/cypress/e2e/patterns/patterns-in-pages.cy.ts +367 -0
- package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.cy.ts +144 -0
- package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.bdd.md +118 -0
- package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.cy.ts +84 -0
- package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.bdd.md +288 -0
- package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.cy.ts +188 -0
- package/tests/cypress/e2e/uat/_core/auth/login-logout.bdd.md +160 -0
- package/tests/cypress/e2e/uat/_core/auth/login-logout.cy.ts +116 -0
- package/tests/cypress/e2e/uat/_core/auth/password-reset.bdd.md +289 -0
- package/tests/cypress/e2e/uat/_core/auth/password-reset.cy.ts +200 -0
- package/tests/cypress/e2e/uat/_core/auth/registration-control-invitation.cy.ts +176 -0
- package/tests/cypress/e2e/uat/_core/auth/registration-control-open.cy.ts +131 -0
- package/tests/cypress/e2e/uat/_core/auth/registration-control.cy.ts +140 -0
- package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.bdd.md +225 -0
- package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.cy.ts +148 -0
- package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.bdd.md +251 -0
- package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.cy.ts +163 -0
- package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.cy.ts +141 -0
- package/tests/cypress/e2e/uat/_core/billing/extended.bdd.md +273 -0
- package/tests/cypress/e2e/uat/_core/billing/extended.cy.ts +209 -0
- package/tests/cypress/e2e/uat/_core/billing/feature-gates.bdd.md +407 -0
- package/tests/cypress/e2e/uat/_core/billing/feature-gates.cy.ts +307 -0
- package/tests/cypress/e2e/uat/_core/billing/page.bdd.md +329 -0
- package/tests/cypress/e2e/uat/_core/billing/page.cy.ts +250 -0
- package/tests/cypress/e2e/uat/_core/billing/status.bdd.md +190 -0
- package/tests/cypress/e2e/uat/_core/billing/status.cy.ts +145 -0
- package/tests/cypress/e2e/uat/_core/billing/team-switch.bdd.md +156 -0
- package/tests/cypress/e2e/uat/_core/billing/team-switch.cy.ts +122 -0
- package/tests/cypress/e2e/uat/_core/billing/usage.bdd.md +218 -0
- package/tests/cypress/e2e/uat/_core/billing/usage.cy.ts +176 -0
- package/tests/cypress/e2e/uat/_core/blocks/hero.bdd.md +124 -0
- package/tests/cypress/e2e/uat/_core/blocks/hero.cy.ts +56 -0
- package/tests/cypress/e2e/uat/_core/devtools/api-tester.cy.ts +390 -0
- package/tests/cypress/e2e/uat/_core/performance/suspense-loading.cy.ts +134 -0
- package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.bdd.md +736 -0
- package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.cy.ts +740 -0
- package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
- package/tests/cypress/e2e/uat/_core/teams/roles-matrix.bdd.md +553 -0
- package/tests/cypress/e2e/uat/_core/teams/roles-matrix.cy.ts +185 -0
- package/tests/cypress/e2e/uat/_core/teams/switcher.bdd.md +1151 -0
- package/tests/cypress/e2e/uat/_core/teams/switcher.cy.ts +497 -0
- package/tests/cypress/e2e/uat/_core/teams/team-switcher.md +198 -0
- package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
- package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
- package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
- package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
- package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
- package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
- package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
- package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
- package/tests/cypress/e2e/uat/features/roles/editor-role.bdd.md +552 -0
- package/tests/cypress/e2e/uat/features/roles/editor-role.cy.ts +210 -0
- package/tests/cypress/e2e/uat/features/roles/member-restrictions.bdd.md +450 -0
- package/tests/cypress/e2e/uat/features/roles/member-restrictions.cy.ts +189 -0
- package/tests/cypress/e2e/uat/features/roles/owner-full-crud.bdd.md +530 -0
- package/tests/cypress/e2e/uat/features/roles/owner-full-crud.cy.ts +247 -0
- package/tests/cypress/fixtures/blocks.json +218 -0
- package/tests/cypress/fixtures/entities.json +87 -0
- package/tests/cypress/fixtures/page-builder.json +21 -0
- package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
- package/tests/cypress/src/components/CustomersPOM.ts +439 -0
- package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
- package/tests/cypress/src/components/EntityForm.ts +375 -0
- package/tests/cypress/src/components/EntityList.ts +389 -0
- package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
- package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
- package/tests/cypress/src/components/PostsListPOM.ts +223 -0
- package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
- package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
- package/tests/cypress/src/components/TasksPOM.ts +272 -0
- package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
- package/tests/cypress/src/components/index.ts +21 -0
- package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
- package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
- package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
- package/tests/cypress/src/controllers/MediaAPIController.js +231 -0
- package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
- package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
- package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
- package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
- package/tests/cypress/src/controllers/index.js +25 -0
- package/tests/cypress/src/core/AuthPOM.ts +450 -0
- package/tests/cypress/src/core/BasePOM.ts +33 -0
- package/tests/cypress/src/core/BlockEditorBasePOM.ts +874 -0
- package/tests/cypress/src/core/DashboardEntityPOM.ts +41 -0
- package/tests/cypress/src/core/index.ts +14 -0
- package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
- package/tests/cypress/src/entities/PagesPOM.ts +137 -0
- package/tests/cypress/src/entities/PatternsPOM.ts +329 -0
- package/tests/cypress/src/entities/PostsPOM.ts +137 -0
- package/tests/cypress/src/entities/TasksPOM.ts +246 -0
- package/tests/cypress/src/entities/index.ts +16 -0
- package/tests/cypress/src/features/BillingPOM.ts +385 -0
- package/tests/cypress/src/features/DashboardPOM.ts +271 -0
- package/tests/cypress/src/features/DevtoolsPOM.ts +750 -0
- package/tests/cypress/src/features/PageBuilderPOM.ts +283 -0
- package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
- package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
- package/tests/cypress/src/features/SettingsPOM.ts +707 -0
- package/tests/cypress/src/features/SuperadminPOM.ts +851 -0
- package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
- package/tests/cypress/src/features/index.ts +28 -0
- package/tests/cypress/src/helpers/ApiInterceptor.ts +20 -0
- package/tests/cypress/src/index.ts +101 -0
- package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
- package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
- package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
- package/tests/cypress/src/pages/dashboard/index.js +6 -0
- package/tests/cypress/src/pages/index.js +5 -0
- package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
- package/tests/cypress/src/pages/public/LandingPage.js +69 -0
- package/tests/cypress/src/pages/public/PricingPage.js +33 -0
- package/tests/cypress/src/pages/public/index.js +6 -0
- package/tests/cypress/src/selectors.ts +46 -0
- package/tests/cypress/src/session-helpers.ts +518 -0
- package/tests/cypress/support/doc-commands.ts +260 -0
- package/tests/cypress/support/e2e.ts +90 -0
- package/tests/cypress.config.ts +178 -0
- package/tests/jest/__mocks__/@nextsparkjs/core/components/ui/badge.js +16 -0
- package/tests/jest/__mocks__/@nextsparkjs/core/lib/db.js +11 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +160 -0
- package/tests/jest/__mocks__/@nextsparkjs/registries/theme-registry.ts +68 -0
- package/tests/jest/__mocks__/jose.js +22 -0
- package/tests/jest/__mocks__/next/image.js +15 -0
- package/tests/jest/__mocks__/next-server.js +56 -0
- package/tests/jest/components/post-header.test.tsx +377 -0
- package/tests/jest/jest.config.cjs +154 -0
- package/tests/jest/langchain/COVERAGE.md +372 -0
- package/tests/jest/langchain/guardrails.test.ts +465 -0
- package/tests/jest/langchain/streaming.test.ts +370 -0
- package/tests/jest/langchain/token-tracker.test.ts +455 -0
- package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
- package/tests/jest/langchain/tracer.test.ts +823 -0
- package/tests/jest/services/tasks.service.test.ts +707 -0
- package/tests/jest/setup.ts +170 -0
- package/tests/jest/tsconfig.jest.json +6 -0
- package/tests/jest/validation/categories.test.ts +429 -0
- package/tests/jest/validation/posts.test.ts +546 -0
- package/tests/tsconfig.json +21 -0
- /package/docs/{02-features → public/02-features}/01-components.md +0 -0
- /package/docs/{02-features → public/02-features}/02-styling.md +0 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CustomerAPIController - Controller for interacting with the Customers API
|
|
3
|
+
* Encapsulates all CRUD operations for /api/v1/customers endpoints
|
|
4
|
+
*
|
|
5
|
+
* Requires:
|
|
6
|
+
* - API Key with customers:read, customers:write scopes (or superadmin with *)
|
|
7
|
+
* - x-team-id header for team context
|
|
8
|
+
*/
|
|
9
|
+
const BaseAPIController = require('./BaseAPIController')
|
|
10
|
+
const entitiesConfig = require('../../fixtures/entities.json')
|
|
11
|
+
|
|
12
|
+
const { slug } = entitiesConfig.entities.customers
|
|
13
|
+
|
|
14
|
+
class CustomerAPIController extends BaseAPIController {
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} baseUrl - Base URL for API requests
|
|
17
|
+
* @param {string|null} apiKey - API key for authentication
|
|
18
|
+
* @param {string|null} teamId - Team ID for x-team-id header
|
|
19
|
+
*/
|
|
20
|
+
constructor(baseUrl = 'http://localhost:5173', apiKey = null, teamId = null) {
|
|
21
|
+
super(baseUrl, apiKey, teamId, { slug })
|
|
22
|
+
// Counter for generating unique account numbers
|
|
23
|
+
this.accountCounter = Date.now()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ============================================================
|
|
27
|
+
// SEMANTIC ALIASES (for backward compatibility)
|
|
28
|
+
// ============================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* GET /api/v1/customers - Get list of customers
|
|
32
|
+
* @param {Object} options - Query options
|
|
33
|
+
* @param {number} [options.page] - Page number
|
|
34
|
+
* @param {number} [options.limit] - Results per page
|
|
35
|
+
* @param {string} [options.office] - Filter by office
|
|
36
|
+
* @param {string} [options.salesRep] - Filter by salesRep
|
|
37
|
+
* @param {string} [options.metas] - Metadata parameter ('all', 'key1,key2', etc.)
|
|
38
|
+
* @param {string} [options.search] - Search in name/account/office
|
|
39
|
+
* @param {Object} [options.headers] - Additional headers
|
|
40
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
41
|
+
*/
|
|
42
|
+
getCustomers(options = {}) {
|
|
43
|
+
return this.list(options)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* GET /api/v1/customers/{id} - Get specific customer by ID
|
|
48
|
+
* @param {string} id - Customer ID
|
|
49
|
+
* @param {Object} options - Additional options
|
|
50
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
51
|
+
*/
|
|
52
|
+
getCustomerById(id, options = {}) {
|
|
53
|
+
return this.getById(id, options)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* POST /api/v1/customers - Create new customer
|
|
58
|
+
* @param {Object} customerData - Customer data
|
|
59
|
+
* @param {string} customerData.name - Customer name (required)
|
|
60
|
+
* @param {number} customerData.account - Account number (required, must be unique)
|
|
61
|
+
* @param {string} customerData.office - Customer office (required)
|
|
62
|
+
* @param {string} [customerData.phone] - Phone number
|
|
63
|
+
* @param {string} [customerData.salesRep] - Sales representative
|
|
64
|
+
* @param {string[]} [customerData.visitDays] - Array of visit days
|
|
65
|
+
* @param {string[]} [customerData.contactDays] - Array of contact days
|
|
66
|
+
* @param {Object} [customerData.metas] - Metadata object
|
|
67
|
+
* @param {Object} options - Additional options
|
|
68
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
69
|
+
*/
|
|
70
|
+
createCustomer(customerData, options = {}) {
|
|
71
|
+
return this.create(customerData, options)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* PATCH /api/v1/customers/{id} - Update customer
|
|
76
|
+
* @param {string} id - Customer ID
|
|
77
|
+
* @param {Object} updateData - Data to update
|
|
78
|
+
* @param {Object} options - Additional options
|
|
79
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
80
|
+
*/
|
|
81
|
+
updateCustomer(id, updateData, options = {}) {
|
|
82
|
+
return this.update(id, updateData, options)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* DELETE /api/v1/customers/{id} - Delete customer
|
|
87
|
+
* @param {string} id - Customer ID
|
|
88
|
+
* @param {Object} options - Additional options
|
|
89
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
90
|
+
*/
|
|
91
|
+
deleteCustomer(id, options = {}) {
|
|
92
|
+
return this.delete(id, options)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ============================================================
|
|
96
|
+
// DATA GENERATORS
|
|
97
|
+
// ============================================================
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Generate a unique account number
|
|
101
|
+
* Uses timestamp + counter to ensure uniqueness
|
|
102
|
+
* @returns {number} Unique account number
|
|
103
|
+
*/
|
|
104
|
+
generateUniqueAccount() {
|
|
105
|
+
this.accountCounter++
|
|
106
|
+
// Use last 9 digits to stay within INTEGER range
|
|
107
|
+
return parseInt(String(this.accountCounter).slice(-9))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Generate random customer data for testing
|
|
112
|
+
* @param {Object} overrides - Specific data to override
|
|
113
|
+
* @returns {Object} Generated customer data
|
|
114
|
+
*/
|
|
115
|
+
generateRandomData(overrides = {}) {
|
|
116
|
+
return this.generateRandomCustomerData(overrides)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Generate random customer data for testing
|
|
121
|
+
* @param {Object} overrides - Specific data to override
|
|
122
|
+
* @returns {Object} Generated customer data
|
|
123
|
+
*/
|
|
124
|
+
generateRandomCustomerData(overrides = {}) {
|
|
125
|
+
const timestamp = Date.now()
|
|
126
|
+
const randomId = Math.random().toString(36).substring(2, 8)
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
name: `Test Customer ${randomId} - ${timestamp}`,
|
|
130
|
+
account: this.generateUniqueAccount(),
|
|
131
|
+
office: `Office-${randomId}`,
|
|
132
|
+
phone: `+1-555-${Math.floor(1000 + Math.random() * 9000)}`,
|
|
133
|
+
salesRep: `Sales Rep ${randomId}`,
|
|
134
|
+
visitDays: [],
|
|
135
|
+
contactDays: [],
|
|
136
|
+
...overrides
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Create a test customer and return its data
|
|
142
|
+
* @param {Object} customerData - Customer data (optional)
|
|
143
|
+
* @returns {Cypress.Chainable} Promise resolving with created customer data
|
|
144
|
+
*/
|
|
145
|
+
createTestCustomer(customerData = {}) {
|
|
146
|
+
return this.createTestEntity(customerData)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Clean up a test customer (delete it)
|
|
151
|
+
* @param {string} id - Customer ID
|
|
152
|
+
* @returns {Cypress.Chainable} Delete response
|
|
153
|
+
*/
|
|
154
|
+
cleanupTestCustomer(id) {
|
|
155
|
+
return this.cleanup(id)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================================
|
|
159
|
+
// ENTITY-SPECIFIC VALIDATORS
|
|
160
|
+
// ============================================================
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Validate customer object structure
|
|
164
|
+
* @param {Object} customer - Customer object
|
|
165
|
+
* @param {boolean} allowMetas - If metas property is allowed
|
|
166
|
+
*/
|
|
167
|
+
validateCustomerObject(customer, allowMetas = false) {
|
|
168
|
+
// Base fields
|
|
169
|
+
this.validateBaseEntityFields(customer)
|
|
170
|
+
|
|
171
|
+
// Required entity fields
|
|
172
|
+
expect(customer).to.have.property('name')
|
|
173
|
+
expect(customer.name).to.be.a('string')
|
|
174
|
+
|
|
175
|
+
expect(customer).to.have.property('account')
|
|
176
|
+
// Account can be number or string depending on how PostgreSQL returns it
|
|
177
|
+
expect(typeof customer.account === 'number' || typeof customer.account === 'string').to.be.true
|
|
178
|
+
|
|
179
|
+
expect(customer).to.have.property('office')
|
|
180
|
+
expect(customer.office).to.be.a('string')
|
|
181
|
+
|
|
182
|
+
// Optional fields that can be null
|
|
183
|
+
if (customer.phone !== null && customer.phone !== undefined) {
|
|
184
|
+
expect(customer.phone).to.be.a('string')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (customer.salesRep !== null && customer.salesRep !== undefined) {
|
|
188
|
+
expect(customer.salesRep).to.be.a('string')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// JSONB array fields
|
|
192
|
+
expect(customer).to.have.property('visitDays')
|
|
193
|
+
expect(customer.visitDays).to.be.an('array')
|
|
194
|
+
|
|
195
|
+
expect(customer).to.have.property('contactDays')
|
|
196
|
+
expect(customer.contactDays).to.be.an('array')
|
|
197
|
+
|
|
198
|
+
// Validate metas if present
|
|
199
|
+
if (allowMetas && customer.hasOwnProperty('metas')) {
|
|
200
|
+
expect(customer.metas).to.be.an('object')
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================================
|
|
205
|
+
// METADATA HELPERS
|
|
206
|
+
// ============================================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Generate sample metadata for testing
|
|
210
|
+
* @param {string} type - Type of metadata ('contactPreferences', 'billing', 'notes')
|
|
211
|
+
* @returns {Object} Sample metadata
|
|
212
|
+
*/
|
|
213
|
+
generateSampleMetadata(type = 'contactPreferences') {
|
|
214
|
+
const sampleMetas = {
|
|
215
|
+
contactPreferences: {
|
|
216
|
+
preferredTime: 'morning',
|
|
217
|
+
preferredChannel: 'phone',
|
|
218
|
+
language: 'es',
|
|
219
|
+
doNotContact: false
|
|
220
|
+
},
|
|
221
|
+
billing: {
|
|
222
|
+
paymentTerms: 'net30',
|
|
223
|
+
creditLimit: 50000,
|
|
224
|
+
taxExempt: false,
|
|
225
|
+
currency: 'USD'
|
|
226
|
+
},
|
|
227
|
+
notes: {
|
|
228
|
+
lastContact: '2025-12-01T10:00:00Z',
|
|
229
|
+
nextFollowUp: '2025-12-15T10:00:00Z',
|
|
230
|
+
accountManager: 'John Doe',
|
|
231
|
+
priority: 'high'
|
|
232
|
+
},
|
|
233
|
+
customFields: {
|
|
234
|
+
industry: 'Technology',
|
|
235
|
+
segment: 'Enterprise',
|
|
236
|
+
referralSource: 'Website',
|
|
237
|
+
loyaltyTier: 'Gold'
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return { [type]: sampleMetas[type] || sampleMetas.contactPreferences }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Export class for use in tests
|
|
246
|
+
module.exports = CustomerAPIController
|
|
247
|
+
|
|
248
|
+
// For global use in Cypress
|
|
249
|
+
if (typeof window !== 'undefined') {
|
|
250
|
+
window.CustomerAPIController = CustomerAPIController
|
|
251
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaAPIController - Controller for interacting with the Media API
|
|
3
|
+
* Encapsulates all CRUD operations for /api/v1/media endpoints
|
|
4
|
+
*
|
|
5
|
+
* Requires:
|
|
6
|
+
* - API Key with media:read, media:write, media:delete scopes (or superadmin with *)
|
|
7
|
+
*/
|
|
8
|
+
const BaseAPIController = require('./BaseAPIController')
|
|
9
|
+
|
|
10
|
+
class MediaAPIController extends BaseAPIController {
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} baseUrl - Base URL for API requests
|
|
13
|
+
* @param {string|null} apiKey - API key for authentication
|
|
14
|
+
*/
|
|
15
|
+
constructor(baseUrl = 'http://localhost:5173', apiKey = null) {
|
|
16
|
+
// Media does not require x-team-id (no team context)
|
|
17
|
+
super(baseUrl, apiKey, null, { slug: 'media' })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ============================================================
|
|
21
|
+
// SEMANTIC ALIASES (for backward compatibility)
|
|
22
|
+
// ============================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* GET /api/v1/media - Get list of media items
|
|
26
|
+
* @param {Object} options - Query options
|
|
27
|
+
* @param {number} [options.limit] - Results per page (default: 20)
|
|
28
|
+
* @param {number} [options.offset] - Offset for pagination (default: 0)
|
|
29
|
+
* @param {string} [options.type] - Filter by type (image, video, document, audio)
|
|
30
|
+
* @param {string} [options.search] - Search in filename
|
|
31
|
+
* @param {string} [options.orderBy] - Sort field (filename, uploadedAt, size, etc.)
|
|
32
|
+
* @param {string} [options.orderDir] - Sort direction (asc, desc)
|
|
33
|
+
* @param {Object} [options.headers] - Additional headers
|
|
34
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
35
|
+
*/
|
|
36
|
+
getMedia(options = {}) {
|
|
37
|
+
return this.list(options)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* GET /api/v1/media/{id} - Get specific media item by ID
|
|
42
|
+
* @param {string} id - Media ID
|
|
43
|
+
* @param {Object} options - Additional options
|
|
44
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
45
|
+
*/
|
|
46
|
+
getMediaById(id, options = {}) {
|
|
47
|
+
return this.getById(id, options)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* PATCH /api/v1/media/{id} - Update media metadata
|
|
52
|
+
* @param {string} id - Media ID
|
|
53
|
+
* @param {Object} updateData - Data to update
|
|
54
|
+
* @param {string} [updateData.alt] - Alt text for image
|
|
55
|
+
* @param {string} [updateData.caption] - Caption for media
|
|
56
|
+
* @param {Object} options - Additional options
|
|
57
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
58
|
+
*/
|
|
59
|
+
updateMedia(id, updateData, options = {}) {
|
|
60
|
+
return this.update(id, updateData, options)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* DELETE /api/v1/media/{id} - Soft delete media item (status -> deleted)
|
|
65
|
+
* @param {string} id - Media ID
|
|
66
|
+
* @param {Object} options - Additional options
|
|
67
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
68
|
+
*/
|
|
69
|
+
deleteMedia(id, options = {}) {
|
|
70
|
+
return this.delete(id, options)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* POST /api/v1/media/upload - Upload file(s)
|
|
75
|
+
* @param {File|File[]} files - File or array of files to upload
|
|
76
|
+
* @param {Object} options - Additional options
|
|
77
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
78
|
+
*/
|
|
79
|
+
uploadMedia(files, options = {}) {
|
|
80
|
+
const { headers = {} } = options
|
|
81
|
+
const formData = new FormData()
|
|
82
|
+
|
|
83
|
+
// Handle single file or array of files
|
|
84
|
+
const fileArray = Array.isArray(files) ? files : [files]
|
|
85
|
+
fileArray.forEach((file) => {
|
|
86
|
+
formData.append('files', file)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Note: For FormData, Content-Type should be auto-set by browser/Cypress
|
|
90
|
+
const uploadHeaders = { ...this.getHeaders(headers) }
|
|
91
|
+
delete uploadHeaders['Content-Type'] // Let Cypress set multipart boundary
|
|
92
|
+
|
|
93
|
+
return cy.request({
|
|
94
|
+
method: 'POST',
|
|
95
|
+
url: `${this.baseUrl}/api/v1/media/upload`,
|
|
96
|
+
headers: uploadHeaders,
|
|
97
|
+
body: formData,
|
|
98
|
+
failOnStatusCode: false
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ============================================================
|
|
103
|
+
// DATA GENERATORS
|
|
104
|
+
// ============================================================
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generate random media data for testing
|
|
108
|
+
* @param {Object} overrides - Specific data to override
|
|
109
|
+
* @returns {Object} Generated media data
|
|
110
|
+
*/
|
|
111
|
+
generateRandomMediaData(overrides = {}) {
|
|
112
|
+
const timestamp = Date.now()
|
|
113
|
+
const randomId = Math.random().toString(36).substring(2, 8)
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
filename: `test-image-${randomId}-${timestamp}.jpg`,
|
|
117
|
+
alt: `Alt text for test image ${randomId}`,
|
|
118
|
+
caption: `Caption for test image ${randomId}`,
|
|
119
|
+
type: 'image',
|
|
120
|
+
size: Math.floor(100000 + Math.random() * 900000), // Random size
|
|
121
|
+
mimeType: 'image/jpeg',
|
|
122
|
+
...overrides
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create a test media item and return its data
|
|
128
|
+
* Note: This creates a DB record without actual file upload (for testing)
|
|
129
|
+
* @param {Object} mediaData - Media data (optional)
|
|
130
|
+
* @returns {Cypress.Chainable} Promise resolving with created media data
|
|
131
|
+
*/
|
|
132
|
+
createTestMedia(mediaData = {}) {
|
|
133
|
+
const data = this.generateRandomMediaData(mediaData)
|
|
134
|
+
return this.create(data).then((response) => {
|
|
135
|
+
if (response.status === 201) {
|
|
136
|
+
return response.body.data
|
|
137
|
+
}
|
|
138
|
+
throw new Error(`Failed to create test media: ${response.status}`)
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Clean up a test media item (delete it)
|
|
144
|
+
* @param {string} id - Media ID
|
|
145
|
+
* @returns {Cypress.Chainable} Delete response
|
|
146
|
+
*/
|
|
147
|
+
cleanupTestMedia(id) {
|
|
148
|
+
return this.deleteMedia(id)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ============================================================
|
|
152
|
+
// ENTITY-SPECIFIC VALIDATORS
|
|
153
|
+
// ============================================================
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Validate media object structure
|
|
157
|
+
* @param {Object} media - Media object
|
|
158
|
+
*/
|
|
159
|
+
validateMediaObject(media) {
|
|
160
|
+
// Base fields
|
|
161
|
+
this.validateBaseEntityFields(media)
|
|
162
|
+
|
|
163
|
+
// Required media fields
|
|
164
|
+
expect(media).to.have.property('filename')
|
|
165
|
+
expect(media.filename).to.be.a('string')
|
|
166
|
+
|
|
167
|
+
expect(media).to.have.property('url')
|
|
168
|
+
expect(media.url).to.be.a('string')
|
|
169
|
+
|
|
170
|
+
expect(media).to.have.property('type')
|
|
171
|
+
expect(media.type).to.be.oneOf(['image', 'video', 'document', 'audio'])
|
|
172
|
+
|
|
173
|
+
expect(media).to.have.property('size')
|
|
174
|
+
expect(media.size).to.be.a('number')
|
|
175
|
+
|
|
176
|
+
expect(media).to.have.property('mimeType')
|
|
177
|
+
expect(media.mimeType).to.be.a('string')
|
|
178
|
+
|
|
179
|
+
expect(media).to.have.property('status')
|
|
180
|
+
expect(media.status).to.be.oneOf(['active', 'deleted'])
|
|
181
|
+
|
|
182
|
+
// Optional fields
|
|
183
|
+
if (media.alt !== null && media.alt !== undefined) {
|
|
184
|
+
expect(media.alt).to.be.a('string')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (media.caption !== null && media.caption !== undefined) {
|
|
188
|
+
expect(media.caption).to.be.a('string')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (media.thumbnailUrl !== null && media.thumbnailUrl !== undefined) {
|
|
192
|
+
expect(media.thumbnailUrl).to.be.a('string')
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Timestamps
|
|
196
|
+
expect(media).to.have.property('uploadedAt')
|
|
197
|
+
if (media.uploadedAt) {
|
|
198
|
+
expect(media.uploadedAt).to.be.a('string')
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Validate upload response structure
|
|
204
|
+
* @param {Object} response - Upload response body
|
|
205
|
+
*/
|
|
206
|
+
validateUploadResponse(response) {
|
|
207
|
+
expect(response).to.have.property('success', true)
|
|
208
|
+
expect(response).to.have.property('urls')
|
|
209
|
+
expect(response.urls).to.be.an('array')
|
|
210
|
+
|
|
211
|
+
expect(response).to.have.property('media')
|
|
212
|
+
expect(response.media).to.be.an('array')
|
|
213
|
+
|
|
214
|
+
expect(response).to.have.property('count')
|
|
215
|
+
expect(response.count).to.be.a('number')
|
|
216
|
+
expect(response.count).to.eq(response.urls.length)
|
|
217
|
+
expect(response.count).to.eq(response.media.length)
|
|
218
|
+
|
|
219
|
+
expect(response).to.have.property('storage')
|
|
220
|
+
expect(response.storage).to.have.property('provider')
|
|
221
|
+
expect(response.storage).to.have.property('bucket')
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Export class for use in tests
|
|
226
|
+
module.exports = MediaAPIController
|
|
227
|
+
|
|
228
|
+
// For global use in Cypress
|
|
229
|
+
if (typeof window !== 'undefined') {
|
|
230
|
+
window.MediaAPIController = MediaAPIController
|
|
231
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PagesAPIController - Controller for interacting with the Pages API
|
|
3
|
+
* Encapsulates all CRUD operations for /api/v1/pages endpoints
|
|
4
|
+
*
|
|
5
|
+
* Requires:
|
|
6
|
+
* - API Key with pages:read, pages:write scopes (or superadmin with *)
|
|
7
|
+
* - x-team-id header for team context
|
|
8
|
+
*
|
|
9
|
+
* Entity characteristics:
|
|
10
|
+
* - Required fields: slug, title, blocks, locale
|
|
11
|
+
* - Optional fields: SEO fields
|
|
12
|
+
* - Unique constraint: slug + locale
|
|
13
|
+
* - Status system: draft, published, scheduled, archived
|
|
14
|
+
* - Uses PATCH for updates
|
|
15
|
+
*/
|
|
16
|
+
const BaseAPIController = require('./BaseAPIController')
|
|
17
|
+
const entitiesConfig = require('../../fixtures/entities.json')
|
|
18
|
+
|
|
19
|
+
const { slug } = entitiesConfig.entities.pages
|
|
20
|
+
|
|
21
|
+
class PagesAPIController extends BaseAPIController {
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} baseUrl - Base URL for API requests
|
|
24
|
+
* @param {string|null} apiKey - API key for authentication
|
|
25
|
+
* @param {string|null} teamId - Team ID for x-team-id header
|
|
26
|
+
*/
|
|
27
|
+
constructor(baseUrl = 'http://localhost:5173', apiKey = null, teamId = null) {
|
|
28
|
+
super(baseUrl, apiKey, teamId, { slug })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// SEMANTIC ALIASES (for better readability in tests)
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* GET /api/v1/pages - List pages
|
|
37
|
+
* @param {Object} options - Query options
|
|
38
|
+
* @param {number} [options.page] - Page number
|
|
39
|
+
* @param {number} [options.limit] - Results per page
|
|
40
|
+
* @param {string} [options.status] - Filter by status (draft, published, scheduled, archived)
|
|
41
|
+
* @param {string} [options.locale] - Filter by locale
|
|
42
|
+
* @param {string} [options.search] - Search in title
|
|
43
|
+
* @param {string} [options.metas] - Metadata parameter
|
|
44
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
45
|
+
*/
|
|
46
|
+
getPages(options = {}) {
|
|
47
|
+
return this.list(options)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* GET /api/v1/pages/{id} - Get page by ID
|
|
52
|
+
* @param {string} id - Page ID
|
|
53
|
+
* @param {Object} options - Additional options
|
|
54
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
55
|
+
*/
|
|
56
|
+
getPageById(id, options = {}) {
|
|
57
|
+
return this.getById(id, options)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* POST /api/v1/pages - Create page
|
|
62
|
+
* @param {Object} pageData - Page data
|
|
63
|
+
* @param {string} pageData.slug - Page slug (required)
|
|
64
|
+
* @param {string} pageData.title - Page title (required)
|
|
65
|
+
* @param {string} pageData.locale - Page locale (required)
|
|
66
|
+
* @param {Array} pageData.blocks - Page blocks (required)
|
|
67
|
+
* @param {string} [pageData.status] - Status (draft, published, scheduled, archived)
|
|
68
|
+
* @param {string} [pageData.seoTitle] - SEO title
|
|
69
|
+
* @param {string} [pageData.seoDescription] - SEO description
|
|
70
|
+
* @param {Object} [pageData.metas] - Metadata object
|
|
71
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
72
|
+
*/
|
|
73
|
+
createPage(pageData, options = {}) {
|
|
74
|
+
return this.create(pageData, options)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* PATCH /api/v1/pages/{id} - Update page
|
|
79
|
+
* @param {string} id - Page ID
|
|
80
|
+
* @param {Object} updateData - Data to update
|
|
81
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
82
|
+
*/
|
|
83
|
+
updatePage(id, updateData, options = {}) {
|
|
84
|
+
return this.update(id, updateData, options)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* DELETE /api/v1/pages/{id} - Delete page
|
|
89
|
+
* @param {string} id - Page ID
|
|
90
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
91
|
+
*/
|
|
92
|
+
deletePage(id, options = {}) {
|
|
93
|
+
return this.delete(id, options)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================
|
|
97
|
+
// DATA GENERATORS
|
|
98
|
+
// ============================================================
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate random page data for testing
|
|
102
|
+
* @param {Object} overrides - Specific data to override
|
|
103
|
+
* @returns {Object} Generated page data
|
|
104
|
+
*/
|
|
105
|
+
generateRandomData(overrides = {}) {
|
|
106
|
+
return PagesAPIController.generateRandomPageData(overrides)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Static method to generate random page data
|
|
111
|
+
* @param {Object} overrides - Specific data to override
|
|
112
|
+
* @returns {Object} Generated page data
|
|
113
|
+
*/
|
|
114
|
+
static generateRandomPageData(overrides = {}) {
|
|
115
|
+
const timestamp = Date.now()
|
|
116
|
+
const randomId = Math.random().toString(36).substring(2, 8)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
slug: `test-page-${randomId}-${timestamp}`,
|
|
120
|
+
title: `Test Page ${randomId}`,
|
|
121
|
+
locale: 'en',
|
|
122
|
+
status: 'draft',
|
|
123
|
+
blocks: [],
|
|
124
|
+
...overrides
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create a test page and return its data
|
|
130
|
+
* @param {Object} overrides - Data overrides
|
|
131
|
+
* @returns {Cypress.Chainable} Promise resolving with created page data
|
|
132
|
+
*/
|
|
133
|
+
createTestPage(overrides = {}) {
|
|
134
|
+
return this.createTestEntity(overrides)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Clean up a test page
|
|
139
|
+
* @param {string} id - Page ID
|
|
140
|
+
* @returns {Cypress.Chainable} Delete response
|
|
141
|
+
*/
|
|
142
|
+
cleanupTestPage(id) {
|
|
143
|
+
return this.cleanup(id)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ============================================================
|
|
147
|
+
// ENTITY-SPECIFIC VALIDATORS
|
|
148
|
+
// ============================================================
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Validate page object structure
|
|
152
|
+
* @param {Object} page - Page object
|
|
153
|
+
* @param {boolean} allowMetas - If metas property is allowed
|
|
154
|
+
*/
|
|
155
|
+
validatePageObject(page, allowMetas = false) {
|
|
156
|
+
// Base fields
|
|
157
|
+
this.validateBaseEntityFields(page)
|
|
158
|
+
|
|
159
|
+
// Required page fields
|
|
160
|
+
expect(page).to.have.property('slug')
|
|
161
|
+
expect(page.slug).to.be.a('string')
|
|
162
|
+
|
|
163
|
+
expect(page).to.have.property('title')
|
|
164
|
+
expect(page.title).to.be.a('string')
|
|
165
|
+
|
|
166
|
+
expect(page).to.have.property('locale')
|
|
167
|
+
expect(page.locale).to.be.a('string')
|
|
168
|
+
|
|
169
|
+
expect(page).to.have.property('blocks')
|
|
170
|
+
expect(page.blocks).to.be.an('array')
|
|
171
|
+
|
|
172
|
+
// Status field (if present)
|
|
173
|
+
if (page.hasOwnProperty('status') && page.status !== null) {
|
|
174
|
+
expect(page.status).to.be.oneOf(['draft', 'published', 'scheduled', 'archived'])
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Metas if allowed
|
|
178
|
+
if (allowMetas && page.hasOwnProperty('metas')) {
|
|
179
|
+
expect(page.metas).to.be.an('object')
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ============================================================
|
|
184
|
+
// METADATA HELPERS
|
|
185
|
+
// ============================================================
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Generate sample metadata for testing
|
|
189
|
+
* @param {string} type - Type of metadata ('seo', 'layout', 'settings')
|
|
190
|
+
* @returns {Object} Sample metadata
|
|
191
|
+
*/
|
|
192
|
+
generateSampleMetadata(type = 'seo') {
|
|
193
|
+
const sampleMetas = {
|
|
194
|
+
seo: {
|
|
195
|
+
seoTitle: 'Custom Page SEO Title',
|
|
196
|
+
seoDescription: 'Custom SEO description for this page',
|
|
197
|
+
seoKeywords: 'test, page, cypress',
|
|
198
|
+
noindex: false,
|
|
199
|
+
nofollow: false,
|
|
200
|
+
ogImage: '/images/og-page.jpg'
|
|
201
|
+
},
|
|
202
|
+
layout: {
|
|
203
|
+
headerStyle: 'transparent',
|
|
204
|
+
footerVisible: true,
|
|
205
|
+
sidebarEnabled: false,
|
|
206
|
+
fullWidth: true
|
|
207
|
+
},
|
|
208
|
+
settings: {
|
|
209
|
+
cacheEnabled: true,
|
|
210
|
+
cacheTTL: 3600,
|
|
211
|
+
authRequired: false,
|
|
212
|
+
analyticsEnabled: true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return { [type]: sampleMetas[type] || sampleMetas.seo }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Export for CommonJS
|
|
221
|
+
module.exports = PagesAPIController
|
|
222
|
+
|
|
223
|
+
// For global use in Cypress
|
|
224
|
+
if (typeof window !== 'undefined') {
|
|
225
|
+
window.PagesAPIController = PagesAPIController
|
|
226
|
+
}
|