@nextsparkjs/theme-default 0.1.0-beta.17 → 0.1.0-beta.171
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,600 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media API - CRUD Tests
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive test suite for Media API endpoints.
|
|
5
|
+
* Tests GET, PATCH, DELETE operations and upload functionality.
|
|
6
|
+
*
|
|
7
|
+
* Entity characteristics:
|
|
8
|
+
* - No team context required (global media library)
|
|
9
|
+
* - Soft delete (status → deleted)
|
|
10
|
+
* - Supports filtering by type, search by filename
|
|
11
|
+
* - Pagination via limit/offset
|
|
12
|
+
* - Sorting via orderBy/orderDir
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/// <reference types="cypress" />
|
|
16
|
+
|
|
17
|
+
import * as allure from 'allure-cypress'
|
|
18
|
+
|
|
19
|
+
const MediaAPIController = require('../../../../src/controllers/MediaAPIController.js')
|
|
20
|
+
|
|
21
|
+
describe('Media API - CRUD Operations', {
|
|
22
|
+
tags: ['@api', '@feat-media-library', '@crud', '@regression']
|
|
23
|
+
}, () => {
|
|
24
|
+
// Test constants
|
|
25
|
+
const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
|
|
26
|
+
const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
|
|
27
|
+
|
|
28
|
+
// Controller instance
|
|
29
|
+
let mediaAPI: InstanceType<typeof MediaAPIController>
|
|
30
|
+
|
|
31
|
+
// Track created media for cleanup
|
|
32
|
+
let createdMedia: any[] = []
|
|
33
|
+
|
|
34
|
+
before(() => {
|
|
35
|
+
// Initialize controller with superadmin credentials
|
|
36
|
+
mediaAPI = new MediaAPIController(BASE_URL, SUPERADMIN_API_KEY)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
allure.epic('API')
|
|
41
|
+
allure.feature('Media Library')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
// Cleanup created media after each test
|
|
46
|
+
createdMedia.forEach((media) => {
|
|
47
|
+
if (media?.id) {
|
|
48
|
+
mediaAPI.deleteMedia(media.id)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
createdMedia = []
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// ============================================
|
|
55
|
+
// GET /api/v1/media - List Media
|
|
56
|
+
// ============================================
|
|
57
|
+
describe('GET /api/v1/media - List Media', () => {
|
|
58
|
+
it('MEDIA_API_001: Should list media with valid API key', { tags: '@smoke' }, () => {
|
|
59
|
+
allure.story('CRUD Operations')
|
|
60
|
+
allure.severity('critical')
|
|
61
|
+
|
|
62
|
+
mediaAPI.getMedia().then((response: any) => {
|
|
63
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
64
|
+
expect(response.body.data).to.be.an('array')
|
|
65
|
+
expect(response.body.info).to.have.property('limit')
|
|
66
|
+
expect(response.body.info).to.have.property('offset')
|
|
67
|
+
expect(response.body.info).to.have.property('total')
|
|
68
|
+
|
|
69
|
+
cy.log(`Found ${response.body.data.length} media items`)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('MEDIA_API_002: Should list media with pagination (limit/offset)', () => {
|
|
74
|
+
mediaAPI.getMedia({ limit: 5, offset: 0 }).then((response: any) => {
|
|
75
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
76
|
+
expect(response.body.info.limit).to.eq(5)
|
|
77
|
+
expect(response.body.info.offset).to.eq(0)
|
|
78
|
+
expect(response.body.data.length).to.be.at.most(5)
|
|
79
|
+
|
|
80
|
+
cy.log(`Page 1 with limit 5: ${response.body.data.length} media items`)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('MEDIA_API_003: Should filter media by type=image', () => {
|
|
85
|
+
mediaAPI.getMedia({ type: 'image' }).then((response: any) => {
|
|
86
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
87
|
+
expect(response.body.data).to.be.an('array')
|
|
88
|
+
|
|
89
|
+
// All returned items should have type=image
|
|
90
|
+
response.body.data.forEach((media: any) => {
|
|
91
|
+
expect(media.type).to.eq('image')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
cy.log(`Found ${response.body.data.length} images`)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('MEDIA_API_004: Should filter media by type=video', () => {
|
|
99
|
+
mediaAPI.getMedia({ type: 'video' }).then((response: any) => {
|
|
100
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
101
|
+
expect(response.body.data).to.be.an('array')
|
|
102
|
+
|
|
103
|
+
// All returned items should have type=video
|
|
104
|
+
response.body.data.forEach((media: any) => {
|
|
105
|
+
expect(media.type).to.eq('video')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
cy.log(`Found ${response.body.data.length} videos`)
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('MEDIA_API_005: Should search media by filename', () => {
|
|
113
|
+
// This test assumes there's existing media with searchable filenames
|
|
114
|
+
// If no media exists, search returns empty array (still valid)
|
|
115
|
+
const searchTerm = 'test'
|
|
116
|
+
|
|
117
|
+
mediaAPI.getMedia({ search: searchTerm }).then((response: any) => {
|
|
118
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
119
|
+
expect(response.body.data).to.be.an('array')
|
|
120
|
+
|
|
121
|
+
cy.log(`Search '${searchTerm}' found ${response.body.data.length} results`)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('MEDIA_API_006: Should sort media by filename (ascending)', () => {
|
|
126
|
+
mediaAPI.getMedia({ orderBy: 'filename', orderDir: 'asc', limit: 10 }).then((response: any) => {
|
|
127
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
128
|
+
expect(response.body.data).to.be.an('array')
|
|
129
|
+
|
|
130
|
+
if (response.body.data.length > 1) {
|
|
131
|
+
// Verify ascending order
|
|
132
|
+
const filenames = response.body.data.map((m: any) => m.filename)
|
|
133
|
+
const sorted = [...filenames].sort()
|
|
134
|
+
expect(filenames).to.deep.eq(sorted)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
cy.log(`Sorted ${response.body.data.length} items by filename (asc)`)
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('MEDIA_API_007: Should sort media by uploadedAt (descending)', () => {
|
|
142
|
+
mediaAPI.getMedia({ orderBy: 'uploadedAt', orderDir: 'desc', limit: 10 }).then((response: any) => {
|
|
143
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
144
|
+
expect(response.body.data).to.be.an('array')
|
|
145
|
+
|
|
146
|
+
if (response.body.data.length > 1) {
|
|
147
|
+
// Verify descending order by uploadedAt
|
|
148
|
+
const timestamps = response.body.data.map((m: any) => new Date(m.uploadedAt).getTime())
|
|
149
|
+
for (let i = 0; i < timestamps.length - 1; i++) {
|
|
150
|
+
expect(timestamps[i]).to.be.at.least(timestamps[i + 1])
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
cy.log(`Sorted ${response.body.data.length} items by uploadedAt (desc)`)
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('MEDIA_API_008: Should return empty array for non-matching search', () => {
|
|
159
|
+
const nonExistentTerm = 'NonExistentMediaFilename123456789'
|
|
160
|
+
|
|
161
|
+
mediaAPI.getMedia({ search: nonExistentTerm }).then((response: any) => {
|
|
162
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
163
|
+
expect(response.body.data).to.be.an('array')
|
|
164
|
+
expect(response.body.data.length).to.eq(0)
|
|
165
|
+
|
|
166
|
+
cy.log('Search with non-matching term returns empty array')
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('MEDIA_API_009: Should reject request without API key', () => {
|
|
171
|
+
const noAuthAPI = new MediaAPIController(BASE_URL, null)
|
|
172
|
+
|
|
173
|
+
noAuthAPI.getMedia().then((response: any) => {
|
|
174
|
+
expect(response.status).to.eq(401)
|
|
175
|
+
expect(response.body).to.have.property('success', false)
|
|
176
|
+
|
|
177
|
+
cy.log('Request without API key rejected with 401')
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// ============================================
|
|
183
|
+
// GET /api/v1/media/{id} - Get Media by ID
|
|
184
|
+
// ============================================
|
|
185
|
+
describe('GET /api/v1/media/{id} - Get Media by ID', () => {
|
|
186
|
+
let testMediaId: string
|
|
187
|
+
|
|
188
|
+
beforeEach(() => {
|
|
189
|
+
// Get first media item from list for testing
|
|
190
|
+
mediaAPI.getMedia({ limit: 1 }).then((response: any) => {
|
|
191
|
+
if (response.body.data.length > 0) {
|
|
192
|
+
testMediaId = response.body.data[0].id
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('MEDIA_API_010: Should get media by valid ID', () => {
|
|
198
|
+
cy.then(() => {
|
|
199
|
+
if (!testMediaId) {
|
|
200
|
+
cy.log('No media items available to test')
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
mediaAPI.getMediaById(testMediaId).then((response: any) => {
|
|
205
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
206
|
+
|
|
207
|
+
const media = response.body.data
|
|
208
|
+
mediaAPI.validateMediaObject(media)
|
|
209
|
+
expect(media.id).to.eq(testMediaId)
|
|
210
|
+
|
|
211
|
+
cy.log(`Retrieved media: ${media.filename}`)
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('MEDIA_API_011: Should return 404 for non-existent media', () => {
|
|
217
|
+
const fakeId = 'non-existent-media-id-12345'
|
|
218
|
+
|
|
219
|
+
mediaAPI.getMediaById(fakeId).then((response: any) => {
|
|
220
|
+
expect(response.status).to.eq(404)
|
|
221
|
+
expect(response.body).to.have.property('success', false)
|
|
222
|
+
|
|
223
|
+
cy.log('Non-existent media returns 404')
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// ============================================
|
|
229
|
+
// PATCH /api/v1/media/{id} - Update Media Metadata
|
|
230
|
+
// ============================================
|
|
231
|
+
describe('PATCH /api/v1/media/{id} - Update Media Metadata', () => {
|
|
232
|
+
let testMediaId: string
|
|
233
|
+
|
|
234
|
+
beforeEach(() => {
|
|
235
|
+
// Get first media item for testing
|
|
236
|
+
mediaAPI.getMedia({ limit: 1 }).then((response: any) => {
|
|
237
|
+
if (response.body.data.length > 0) {
|
|
238
|
+
testMediaId = response.body.data[0].id
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('MEDIA_API_020: Should update media alt text', () => {
|
|
244
|
+
cy.then(() => {
|
|
245
|
+
if (!testMediaId) {
|
|
246
|
+
cy.log('No media items available to test')
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const updateData = {
|
|
251
|
+
alt: 'Updated alt text for testing'
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
mediaAPI.updateMedia(testMediaId, updateData).then((response: any) => {
|
|
255
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
256
|
+
|
|
257
|
+
const media = response.body.data
|
|
258
|
+
expect(media.alt).to.eq(updateData.alt)
|
|
259
|
+
|
|
260
|
+
cy.log(`Updated media alt: ${media.alt}`)
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('MEDIA_API_021: Should update media caption', () => {
|
|
266
|
+
cy.then(() => {
|
|
267
|
+
if (!testMediaId) {
|
|
268
|
+
cy.log('No media items available to test')
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const updateData = {
|
|
273
|
+
caption: 'Updated caption for testing'
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
mediaAPI.updateMedia(testMediaId, updateData).then((response: any) => {
|
|
277
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
278
|
+
|
|
279
|
+
const media = response.body.data
|
|
280
|
+
expect(media.caption).to.eq(updateData.caption)
|
|
281
|
+
|
|
282
|
+
cy.log(`Updated media caption: ${media.caption}`)
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('MEDIA_API_022: Should update both alt and caption', () => {
|
|
288
|
+
cy.then(() => {
|
|
289
|
+
if (!testMediaId) {
|
|
290
|
+
cy.log('No media items available to test')
|
|
291
|
+
return
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const updateData = {
|
|
295
|
+
alt: 'Updated alt text',
|
|
296
|
+
caption: 'Updated caption'
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
mediaAPI.updateMedia(testMediaId, updateData).then((response: any) => {
|
|
300
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
301
|
+
|
|
302
|
+
const media = response.body.data
|
|
303
|
+
expect(media.alt).to.eq(updateData.alt)
|
|
304
|
+
expect(media.caption).to.eq(updateData.caption)
|
|
305
|
+
|
|
306
|
+
cy.log(`Updated both alt and caption`)
|
|
307
|
+
})
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it('MEDIA_API_023: Should return 404 for non-existent media', () => {
|
|
312
|
+
const fakeId = 'non-existent-media-id-12345'
|
|
313
|
+
|
|
314
|
+
mediaAPI.updateMedia(fakeId, { alt: 'New Alt' }).then((response: any) => {
|
|
315
|
+
expect(response.status).to.eq(404)
|
|
316
|
+
expect(response.body).to.have.property('success', false)
|
|
317
|
+
|
|
318
|
+
cy.log('Update non-existent media returns 404')
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('MEDIA_API_024: Should reject empty update body', () => {
|
|
323
|
+
cy.then(() => {
|
|
324
|
+
if (!testMediaId) {
|
|
325
|
+
cy.log('No media items available to test')
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
mediaAPI.updateMedia(testMediaId, {}).then((response: any) => {
|
|
330
|
+
expect(response.status).to.eq(400)
|
|
331
|
+
expect(response.body).to.have.property('success', false)
|
|
332
|
+
|
|
333
|
+
cy.log('Empty update body rejected')
|
|
334
|
+
})
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('MEDIA_API_025: Should reject invalid update fields (only alt/caption allowed)', () => {
|
|
339
|
+
cy.then(() => {
|
|
340
|
+
if (!testMediaId) {
|
|
341
|
+
cy.log('No media items available to test')
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Try to update filename (should be rejected)
|
|
346
|
+
const invalidUpdate = {
|
|
347
|
+
filename: 'hacked-filename.jpg'
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
mediaAPI.updateMedia(testMediaId, invalidUpdate).then((response: any) => {
|
|
351
|
+
// Should return 400 (validation error) or ignore the invalid field
|
|
352
|
+
// Backend should only allow alt/caption updates
|
|
353
|
+
expect(response.status).to.be.oneOf([200, 400])
|
|
354
|
+
|
|
355
|
+
if (response.status === 200) {
|
|
356
|
+
// If 200, filename should NOT be changed
|
|
357
|
+
expect(response.body.data.filename).to.not.eq(invalidUpdate.filename)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
cy.log('Invalid field update handled correctly')
|
|
361
|
+
})
|
|
362
|
+
})
|
|
363
|
+
})
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
// ============================================
|
|
367
|
+
// DELETE /api/v1/media/{id} - Soft Delete Media
|
|
368
|
+
// ============================================
|
|
369
|
+
describe('DELETE /api/v1/media/{id} - Soft Delete Media', () => {
|
|
370
|
+
let testMediaId: string
|
|
371
|
+
|
|
372
|
+
beforeEach(() => {
|
|
373
|
+
// Get a media item that can be deleted
|
|
374
|
+
mediaAPI.getMedia({ limit: 1, type: 'image' }).then((response: any) => {
|
|
375
|
+
if (response.body.data.length > 0) {
|
|
376
|
+
testMediaId = response.body.data[0].id
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
it('MEDIA_API_030: Should soft delete media by valid ID', () => {
|
|
382
|
+
cy.then(() => {
|
|
383
|
+
if (!testMediaId) {
|
|
384
|
+
cy.log('No media items available to test deletion')
|
|
385
|
+
return
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Delete the media
|
|
389
|
+
mediaAPI.deleteMedia(testMediaId).then((response: any) => {
|
|
390
|
+
mediaAPI.validateSuccessResponse(response, 200)
|
|
391
|
+
expect(response.body.data).to.have.property('success', true)
|
|
392
|
+
expect(response.body.data).to.have.property('id', testMediaId)
|
|
393
|
+
|
|
394
|
+
cy.log(`Soft deleted media: ${testMediaId}`)
|
|
395
|
+
|
|
396
|
+
// Verify soft delete: item should return 404 or status=deleted
|
|
397
|
+
mediaAPI.getMediaById(testMediaId).then((getResponse: any) => {
|
|
398
|
+
// After soft delete, GET should return 404 (filtered out)
|
|
399
|
+
expect(getResponse.status).to.eq(404)
|
|
400
|
+
|
|
401
|
+
cy.log('Verified soft deletion - media no longer accessible')
|
|
402
|
+
})
|
|
403
|
+
})
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
it('MEDIA_API_031: Should return 404 for non-existent media', () => {
|
|
408
|
+
const fakeId = 'non-existent-media-id-12345'
|
|
409
|
+
|
|
410
|
+
mediaAPI.deleteMedia(fakeId).then((response: any) => {
|
|
411
|
+
expect(response.status).to.eq(404)
|
|
412
|
+
expect(response.body).to.have.property('success', false)
|
|
413
|
+
|
|
414
|
+
cy.log('Delete non-existent media returns 404')
|
|
415
|
+
})
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
it('MEDIA_API_032: Should return 404 when deleting already deleted media', () => {
|
|
419
|
+
cy.then(() => {
|
|
420
|
+
if (!testMediaId) {
|
|
421
|
+
cy.log('No media items available to test')
|
|
422
|
+
return
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Delete once
|
|
426
|
+
mediaAPI.deleteMedia(testMediaId).then((deleteResponse: any) => {
|
|
427
|
+
expect(deleteResponse.status).to.eq(200)
|
|
428
|
+
|
|
429
|
+
// Try to delete again
|
|
430
|
+
mediaAPI.deleteMedia(testMediaId).then((secondDeleteResponse: any) => {
|
|
431
|
+
expect(secondDeleteResponse.status).to.eq(404)
|
|
432
|
+
expect(secondDeleteResponse.body).to.have.property('success', false)
|
|
433
|
+
|
|
434
|
+
cy.log('Second delete attempt correctly returns 404')
|
|
435
|
+
})
|
|
436
|
+
})
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
// ============================================
|
|
442
|
+
// Authentication Tests
|
|
443
|
+
// ============================================
|
|
444
|
+
describe('Authentication', () => {
|
|
445
|
+
it('MEDIA_API_040: Should accept session authentication', () => {
|
|
446
|
+
// Login as superadmin and use session cookies
|
|
447
|
+
cy.login('superadmin@tmt.dev', 'Test1234')
|
|
448
|
+
|
|
449
|
+
cy.request({
|
|
450
|
+
method: 'GET',
|
|
451
|
+
url: `${BASE_URL}/api/v1/media`,
|
|
452
|
+
failOnStatusCode: false
|
|
453
|
+
}).then((response) => {
|
|
454
|
+
expect(response.status).to.eq(200)
|
|
455
|
+
expect(response.body.success).to.be.true
|
|
456
|
+
|
|
457
|
+
cy.log('Session authentication successful')
|
|
458
|
+
})
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
it('MEDIA_API_041: Should accept API key authentication', () => {
|
|
462
|
+
cy.request({
|
|
463
|
+
method: 'GET',
|
|
464
|
+
url: `${BASE_URL}/api/v1/media`,
|
|
465
|
+
headers: {
|
|
466
|
+
'Authorization': `Bearer ${SUPERADMIN_API_KEY}`
|
|
467
|
+
},
|
|
468
|
+
failOnStatusCode: false
|
|
469
|
+
}).then((response) => {
|
|
470
|
+
expect(response.status).to.eq(200)
|
|
471
|
+
expect(response.body.success).to.be.true
|
|
472
|
+
|
|
473
|
+
cy.log('API key authentication successful')
|
|
474
|
+
})
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
it('MEDIA_API_042: Should return 401 without authentication', () => {
|
|
478
|
+
cy.request({
|
|
479
|
+
method: 'GET',
|
|
480
|
+
url: `${BASE_URL}/api/v1/media`,
|
|
481
|
+
failOnStatusCode: false
|
|
482
|
+
}).then((response) => {
|
|
483
|
+
expect(response.status).to.eq(401)
|
|
484
|
+
expect(response.body.success).to.be.false
|
|
485
|
+
|
|
486
|
+
cy.log('No authentication returns 401')
|
|
487
|
+
})
|
|
488
|
+
})
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
// ============================================
|
|
492
|
+
// Edge Cases & Error Handling
|
|
493
|
+
// ============================================
|
|
494
|
+
describe('Edge Cases & Error Handling', () => {
|
|
495
|
+
it('MEDIA_API_050: Should handle invalid limit parameter', () => {
|
|
496
|
+
mediaAPI.getMedia({ limit: -1 }).then((response: any) => {
|
|
497
|
+
// Backend should either reject with 400 or use default limit
|
|
498
|
+
expect(response.status).to.be.oneOf([200, 400])
|
|
499
|
+
|
|
500
|
+
if (response.status === 200) {
|
|
501
|
+
// If accepted, should use default limit (not negative)
|
|
502
|
+
expect(response.body.info.limit).to.be.greaterThan(0)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
cy.log('Invalid limit handled correctly')
|
|
506
|
+
})
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
it('MEDIA_API_051: Should handle invalid offset parameter', () => {
|
|
510
|
+
mediaAPI.getMedia({ offset: -1 }).then((response: any) => {
|
|
511
|
+
// Backend should either reject with 400 or use default offset (0)
|
|
512
|
+
expect(response.status).to.be.oneOf([200, 400])
|
|
513
|
+
|
|
514
|
+
if (response.status === 200) {
|
|
515
|
+
expect(response.body.info.offset).to.be.at.least(0)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
cy.log('Invalid offset handled correctly')
|
|
519
|
+
})
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
it('MEDIA_API_052: Should handle invalid type filter', () => {
|
|
523
|
+
mediaAPI.getMedia({ type: 'invalid-type' }).then((response: any) => {
|
|
524
|
+
// Backend should either reject with 400 or return empty results
|
|
525
|
+
expect(response.status).to.be.oneOf([200, 400])
|
|
526
|
+
|
|
527
|
+
if (response.status === 200) {
|
|
528
|
+
// Should return empty array for invalid type
|
|
529
|
+
expect(response.body.data).to.be.an('array')
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
cy.log('Invalid type filter handled correctly')
|
|
533
|
+
})
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
it('MEDIA_API_053: Should handle invalid orderBy field', () => {
|
|
537
|
+
mediaAPI.getMedia({ orderBy: 'invalid-field' }).then((response: any) => {
|
|
538
|
+
// Backend should either reject with 400 or ignore invalid orderBy
|
|
539
|
+
expect(response.status).to.be.oneOf([200, 400])
|
|
540
|
+
|
|
541
|
+
cy.log('Invalid orderBy handled correctly')
|
|
542
|
+
})
|
|
543
|
+
})
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
// ============================================
|
|
547
|
+
// Integration - Read -> Update -> Delete Lifecycle
|
|
548
|
+
// ============================================
|
|
549
|
+
describe('Integration - Read -> Update -> Delete Lifecycle', () => {
|
|
550
|
+
it('MEDIA_API_100: Should complete lifecycle: Read -> Update -> Delete', () => {
|
|
551
|
+
// Get first available media item
|
|
552
|
+
mediaAPI.getMedia({ limit: 1 }).then((listResponse: any) => {
|
|
553
|
+
if (listResponse.body.data.length === 0) {
|
|
554
|
+
cy.log('No media items available for lifecycle test')
|
|
555
|
+
return
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const mediaId = listResponse.body.data[0].id
|
|
559
|
+
|
|
560
|
+
// 1. READ
|
|
561
|
+
mediaAPI.getMediaById(mediaId).then((readResponse: any) => {
|
|
562
|
+
mediaAPI.validateSuccessResponse(readResponse, 200)
|
|
563
|
+
const originalFilename = readResponse.body.data.filename
|
|
564
|
+
|
|
565
|
+
cy.log(`1. Read media: ${originalFilename}`)
|
|
566
|
+
|
|
567
|
+
// 2. UPDATE
|
|
568
|
+
const updateData = {
|
|
569
|
+
alt: 'Lifecycle Test Alt',
|
|
570
|
+
caption: 'Lifecycle Test Caption'
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
mediaAPI.updateMedia(mediaId, updateData).then((updateResponse: any) => {
|
|
574
|
+
mediaAPI.validateSuccessResponse(updateResponse, 200)
|
|
575
|
+
expect(updateResponse.body.data.alt).to.eq(updateData.alt)
|
|
576
|
+
expect(updateResponse.body.data.caption).to.eq(updateData.caption)
|
|
577
|
+
|
|
578
|
+
cy.log(`2. Updated media metadata`)
|
|
579
|
+
|
|
580
|
+
// 3. DELETE
|
|
581
|
+
mediaAPI.deleteMedia(mediaId).then((deleteResponse: any) => {
|
|
582
|
+
mediaAPI.validateSuccessResponse(deleteResponse, 200)
|
|
583
|
+
expect(deleteResponse.body.data).to.have.property('success', true)
|
|
584
|
+
|
|
585
|
+
cy.log(`3. Deleted media: ${mediaId}`)
|
|
586
|
+
|
|
587
|
+
// 4. VERIFY DELETION
|
|
588
|
+
mediaAPI.getMediaById(mediaId).then((verifyResponse: any) => {
|
|
589
|
+
expect(verifyResponse.status).to.eq(404)
|
|
590
|
+
|
|
591
|
+
cy.log('4. Verified deletion - media no longer accessible')
|
|
592
|
+
cy.log('Full lifecycle completed successfully!')
|
|
593
|
+
})
|
|
594
|
+
})
|
|
595
|
+
})
|
|
596
|
+
})
|
|
597
|
+
})
|
|
598
|
+
})
|
|
599
|
+
})
|
|
600
|
+
})
|