@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.
Files changed (332) hide show
  1. package/api/ai/chat/stream/route.ts +4 -1
  2. package/api/ai/orchestrator/route.ts +10 -3
  3. package/api/ai/single-agent/route.ts +10 -3
  4. package/api/ai/usage/route.ts +4 -1
  5. package/blocks/benefits/component.tsx +4 -4
  6. package/blocks/cta-section/component.tsx +4 -4
  7. package/blocks/faq-accordion/component.tsx +2 -2
  8. package/blocks/features-grid/component.tsx +5 -5
  9. package/blocks/hero/component.tsx +2 -2
  10. package/blocks/hero/fields.ts +1 -1
  11. package/blocks/hero-with-form/component.tsx +7 -7
  12. package/blocks/hero-with-form/fields.ts +1 -1
  13. package/blocks/jumbotron/component.tsx +7 -7
  14. package/blocks/jumbotron/fields.ts +1 -1
  15. package/blocks/logo-cloud/component.tsx +6 -6
  16. package/blocks/logo-cloud/fields.ts +1 -1
  17. package/blocks/post-content/component.tsx +2 -2
  18. package/blocks/pricing-table/component.tsx +5 -5
  19. package/blocks/split-content/component.tsx +5 -5
  20. package/blocks/split-content/fields.ts +1 -1
  21. package/blocks/stats-counter/component.tsx +9 -9
  22. package/blocks/testimonials/component.tsx +4 -4
  23. package/blocks/testimonials/fields.ts +1 -1
  24. package/blocks/text-content/component.tsx +12 -10
  25. package/blocks/timeline/component.tsx +12 -12
  26. package/blocks/video-hero/component.tsx +7 -7
  27. package/blocks/video-hero/fields.ts +1 -1
  28. package/components/ai-chat/ChatPanel.tsx +7 -7
  29. package/components/ai-chat/Message.tsx +2 -2
  30. package/components/ai-chat/MessageInput.tsx +3 -3
  31. package/components/ai-chat/MessageList.tsx +3 -3
  32. package/components/ai-chat/TypingIndicator.tsx +2 -2
  33. package/config/app.config.ts +90 -62
  34. package/config/billing.config.ts +5 -10
  35. package/config/dashboard.config.ts +14 -0
  36. package/config/features.config.ts +10 -0
  37. package/config/permissions.config.ts +22 -23
  38. package/docs/{01-overview → public/01-overview}/01-introduction.md +5 -0
  39. package/docs/{01-overview → public/01-overview}/02-customization.md +5 -0
  40. package/docs/{02-features → public/02-features}/03-tasks-entity.md +5 -0
  41. package/docs/{03-ai → public/03-ai}/01-overview.md +5 -0
  42. package/docs/{03-ai → public/03-ai}/02-customization.md +5 -0
  43. package/docs/superadmin/01-setup/01-configuration.md +79 -0
  44. package/docs/superadmin/01-setup/02-deployment.md +82 -0
  45. package/docs/superadmin/02-management/01-users.md +83 -0
  46. package/docs/superadmin/03-integrations/01-langchain.md +139 -0
  47. package/emails/verify-email.ts +33 -0
  48. package/entities/customers/api/docs.md +107 -0
  49. package/entities/customers/api/presets.ts +80 -0
  50. package/entities/pages/api/docs.md +114 -0
  51. package/entities/pages/api/presets.ts +72 -0
  52. package/entities/posts/api/docs.md +120 -0
  53. package/entities/posts/api/presets.ts +74 -0
  54. package/entities/tasks/api/docs.md +126 -0
  55. package/entities/tasks/api/presets.ts +84 -0
  56. package/lib/selectors.ts +7 -4
  57. package/messages/de/admin.json +45 -0
  58. package/messages/en/admin.json +56 -0
  59. package/messages/en/navigation.json +2 -1
  60. package/messages/es/admin.json +56 -0
  61. package/messages/es/navigation.json +2 -1
  62. package/messages/fr/admin.json +45 -0
  63. package/messages/it/admin.json +45 -0
  64. package/messages/pt/admin.json +45 -0
  65. package/migrations/089_add_editor_team_role.sql +15 -23
  66. package/migrations/090_demo_users_teams.sql +11 -11
  67. package/migrations/091_greek_teams_billing.sql +15 -15
  68. package/migrations/093_pages_sample_data.sql +7 -7
  69. package/migrations/098_patterns_sample_data.sql +234 -0
  70. package/nextsparkjs-theme-default-0.1.0-beta.137.tgz +0 -0
  71. package/package.json +4 -3
  72. package/styles/globals.css +42 -0
  73. package/templates/(public)/page.tsx +1 -1
  74. package/tests/cypress/e2e/_utils/devtools/access.bdd.md +262 -0
  75. package/tests/cypress/e2e/_utils/devtools/access.cy.ts +171 -0
  76. package/tests/cypress/e2e/_utils/devtools/navigation.bdd.md +261 -0
  77. package/tests/cypress/e2e/_utils/devtools/navigation.cy.ts +157 -0
  78. package/tests/cypress/e2e/_utils/devtools/pages.bdd.md +303 -0
  79. package/tests/cypress/e2e/_utils/devtools/pages.cy.ts +184 -0
  80. package/tests/cypress/e2e/_utils/docs/README.md +215 -0
  81. package/tests/cypress/e2e/_utils/selectors/auth.bdd.md +354 -0
  82. package/tests/cypress/e2e/_utils/selectors/auth.cy.ts +310 -0
  83. package/tests/cypress/e2e/_utils/selectors/billing.bdd.md +276 -0
  84. package/tests/cypress/e2e/_utils/selectors/billing.cy.ts +182 -0
  85. package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +615 -0
  86. package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +783 -0
  87. package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
  88. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.bdd.md +205 -0
  89. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +137 -0
  90. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.bdd.md +147 -0
  91. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +114 -0
  92. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +76 -0
  93. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +68 -0
  94. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +326 -0
  95. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +177 -0
  96. package/tests/cypress/e2e/_utils/selectors/devtools.bdd.md +306 -0
  97. package/tests/cypress/e2e/_utils/selectors/devtools.cy.ts +273 -0
  98. package/tests/cypress/e2e/_utils/selectors/global-search.bdd.md +115 -0
  99. package/tests/cypress/e2e/_utils/selectors/global-search.cy.ts +93 -0
  100. package/tests/cypress/e2e/_utils/selectors/patterns.bdd.md +388 -0
  101. package/tests/cypress/e2e/_utils/selectors/patterns.cy.ts +559 -0
  102. package/tests/cypress/e2e/_utils/selectors/public.cy.ts +112 -0
  103. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.bdd.md +266 -0
  104. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.cy.ts +233 -0
  105. package/tests/cypress/e2e/_utils/selectors/settings-billing.bdd.md +78 -0
  106. package/tests/cypress/e2e/_utils/selectors/settings-billing.cy.ts +108 -0
  107. package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +129 -0
  108. package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +115 -0
  109. package/tests/cypress/e2e/_utils/selectors/settings-password.bdd.md +82 -0
  110. package/tests/cypress/e2e/_utils/selectors/settings-password.cy.ts +74 -0
  111. package/tests/cypress/e2e/_utils/selectors/settings-profile.bdd.md +77 -0
  112. package/tests/cypress/e2e/_utils/selectors/settings-profile.cy.ts +79 -0
  113. package/tests/cypress/e2e/_utils/selectors/settings-teams.bdd.md +130 -0
  114. package/tests/cypress/e2e/_utils/selectors/settings-teams.cy.ts +86 -0
  115. package/tests/cypress/e2e/_utils/selectors/superadmin.bdd.md +261 -0
  116. package/tests/cypress/e2e/_utils/selectors/superadmin.cy.ts +193 -0
  117. package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +593 -0
  118. package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +864 -0
  119. package/tests/cypress/e2e/_utils/selectors/taxonomies.cy.ts +126 -0
  120. package/tests/cypress/e2e/_utils/selectors/teams.bdd.md +278 -0
  121. package/tests/cypress/e2e/_utils/selectors/teams.cy.ts +195 -0
  122. package/tests/cypress/e2e/_utils/superadmin/all-teams.bdd.md +261 -0
  123. package/tests/cypress/e2e/_utils/superadmin/all-teams.cy.ts +177 -0
  124. package/tests/cypress/e2e/_utils/superadmin/all-users.bdd.md +406 -0
  125. package/tests/cypress/e2e/_utils/superadmin/all-users.cy.ts +294 -0
  126. package/tests/cypress/e2e/_utils/superadmin/dashboard.bdd.md +235 -0
  127. package/tests/cypress/e2e/_utils/superadmin/dashboard.cy.ts +149 -0
  128. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.bdd.md +290 -0
  129. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.cy.ts +194 -0
  130. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  131. package/tests/cypress/e2e/ai/chat-api.cy.ts +119 -0
  132. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  133. package/tests/cypress/e2e/api/_core/billing/BillingAPIController.js +319 -0
  134. package/tests/cypress/e2e/api/_core/billing/check-action.cy.ts +326 -0
  135. package/tests/cypress/e2e/api/_core/billing/checkout.cy.ts +358 -0
  136. package/tests/cypress/e2e/api/_core/billing/lifecycle.cy.ts +423 -0
  137. package/tests/cypress/e2e/api/_core/billing/plans/README.md +345 -0
  138. package/tests/cypress/e2e/api/_core/billing/plans/business.cy.ts +412 -0
  139. package/tests/cypress/e2e/api/_core/billing/plans/downgrade.cy.ts +510 -0
  140. package/tests/cypress/e2e/api/_core/billing/plans/fixtures/billing-plans.json +163 -0
  141. package/tests/cypress/e2e/api/_core/billing/plans/free.cy.ts +500 -0
  142. package/tests/cypress/e2e/api/_core/billing/plans/pro.cy.ts +497 -0
  143. package/tests/cypress/e2e/api/_core/billing/plans/starter.cy.ts +342 -0
  144. package/tests/cypress/e2e/api/_core/billing/portal.cy.ts +313 -0
  145. package/tests/cypress/e2e/api/_core/devtools/registries.bdd.md +300 -0
  146. package/tests/cypress/e2e/api/_core/devtools/registries.cy.ts +368 -0
  147. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.bdd.md +375 -0
  148. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.cy.ts +346 -0
  149. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  150. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  151. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.bdd.md +649 -0
  152. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.cy.ts +333 -0
  153. package/tests/cypress/e2e/api/_core/security/security-headers.cy.ts +601 -0
  154. package/tests/cypress/e2e/api/_core/settings/api-keys.crud.cy.ts +923 -0
  155. package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
  156. package/tests/cypress/e2e/api/_core/users/users-crud.cy.ts +469 -0
  157. package/tests/cypress/e2e/api/_core/users/users-metas.cy.ts +913 -0
  158. package/tests/cypress/e2e/api/_core/users/users-security.cy.ts +375 -0
  159. package/tests/cypress/e2e/api/entities/customers/customers-crud.cy.ts +648 -0
  160. package/tests/cypress/e2e/api/entities/customers/customers-metas.cy.ts +839 -0
  161. package/tests/cypress/e2e/api/entities/media/media-crud.cy.ts +600 -0
  162. package/tests/cypress/e2e/api/entities/media/media-role-permissions.cy.ts +617 -0
  163. package/tests/cypress/e2e/api/entities/media/media-team-isolation.cy.ts +464 -0
  164. package/tests/cypress/e2e/api/entities/pages/blocks-scope.cy.ts +396 -0
  165. package/tests/cypress/e2e/api/entities/pages/pages-crud.cy.ts +425 -0
  166. package/tests/cypress/e2e/api/entities/pages/pages-status.cy.ts +335 -0
  167. package/tests/cypress/e2e/api/entities/posts/post-categories-crud.cy.ts +610 -0
  168. package/tests/cypress/e2e/api/entities/posts/posts-crud.cy.ts +709 -0
  169. package/tests/cypress/e2e/api/entities/posts/posts-status.cy.ts +396 -0
  170. package/tests/cypress/e2e/api/entities/tasks/tasks-crud.cy.ts +602 -0
  171. package/tests/cypress/e2e/api/entities/tasks/tasks-metas.cy.ts +878 -0
  172. package/tests/cypress/e2e/patterns/patterns-in-pages.cy.ts +367 -0
  173. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.bdd.md +231 -0
  174. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.cy.ts +144 -0
  175. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.bdd.md +118 -0
  176. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.cy.ts +84 -0
  177. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.bdd.md +288 -0
  178. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.cy.ts +188 -0
  179. package/tests/cypress/e2e/uat/_core/auth/login-logout.bdd.md +160 -0
  180. package/tests/cypress/e2e/uat/_core/auth/login-logout.cy.ts +116 -0
  181. package/tests/cypress/e2e/uat/_core/auth/password-reset.bdd.md +289 -0
  182. package/tests/cypress/e2e/uat/_core/auth/password-reset.cy.ts +200 -0
  183. package/tests/cypress/e2e/uat/_core/auth/registration-control-invitation.cy.ts +176 -0
  184. package/tests/cypress/e2e/uat/_core/auth/registration-control-open.cy.ts +131 -0
  185. package/tests/cypress/e2e/uat/_core/auth/registration-control.cy.ts +140 -0
  186. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.bdd.md +225 -0
  187. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.cy.ts +148 -0
  188. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.bdd.md +251 -0
  189. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.cy.ts +163 -0
  190. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.bdd.md +231 -0
  191. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.cy.ts +141 -0
  192. package/tests/cypress/e2e/uat/_core/billing/extended.bdd.md +273 -0
  193. package/tests/cypress/e2e/uat/_core/billing/extended.cy.ts +209 -0
  194. package/tests/cypress/e2e/uat/_core/billing/feature-gates.bdd.md +407 -0
  195. package/tests/cypress/e2e/uat/_core/billing/feature-gates.cy.ts +307 -0
  196. package/tests/cypress/e2e/uat/_core/billing/page.bdd.md +329 -0
  197. package/tests/cypress/e2e/uat/_core/billing/page.cy.ts +250 -0
  198. package/tests/cypress/e2e/uat/_core/billing/status.bdd.md +190 -0
  199. package/tests/cypress/e2e/uat/_core/billing/status.cy.ts +145 -0
  200. package/tests/cypress/e2e/uat/_core/billing/team-switch.bdd.md +156 -0
  201. package/tests/cypress/e2e/uat/_core/billing/team-switch.cy.ts +122 -0
  202. package/tests/cypress/e2e/uat/_core/billing/usage.bdd.md +218 -0
  203. package/tests/cypress/e2e/uat/_core/billing/usage.cy.ts +176 -0
  204. package/tests/cypress/e2e/uat/_core/blocks/hero.bdd.md +124 -0
  205. package/tests/cypress/e2e/uat/_core/blocks/hero.cy.ts +56 -0
  206. package/tests/cypress/e2e/uat/_core/devtools/api-tester.cy.ts +390 -0
  207. package/tests/cypress/e2e/uat/_core/performance/suspense-loading.cy.ts +134 -0
  208. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.bdd.md +736 -0
  209. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.cy.ts +740 -0
  210. package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
  211. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.bdd.md +553 -0
  212. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.cy.ts +185 -0
  213. package/tests/cypress/e2e/uat/_core/teams/switcher.bdd.md +1151 -0
  214. package/tests/cypress/e2e/uat/_core/teams/switcher.cy.ts +497 -0
  215. package/tests/cypress/e2e/uat/_core/teams/team-switcher.md +198 -0
  216. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  217. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  218. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  219. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  220. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  221. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  222. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  223. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  224. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  225. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  226. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  227. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  228. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  229. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  230. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  231. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  232. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  233. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  234. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  235. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  236. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  237. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  238. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  239. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  240. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  241. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  242. package/tests/cypress/e2e/uat/features/roles/editor-role.bdd.md +552 -0
  243. package/tests/cypress/e2e/uat/features/roles/editor-role.cy.ts +210 -0
  244. package/tests/cypress/e2e/uat/features/roles/member-restrictions.bdd.md +450 -0
  245. package/tests/cypress/e2e/uat/features/roles/member-restrictions.cy.ts +189 -0
  246. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.bdd.md +530 -0
  247. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.cy.ts +247 -0
  248. package/tests/cypress/fixtures/blocks.json +218 -0
  249. package/tests/cypress/fixtures/entities.json +87 -0
  250. package/tests/cypress/fixtures/page-builder.json +21 -0
  251. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  252. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  253. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  254. package/tests/cypress/src/components/EntityForm.ts +375 -0
  255. package/tests/cypress/src/components/EntityList.ts +389 -0
  256. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  257. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  258. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  259. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  260. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  261. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  262. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  263. package/tests/cypress/src/components/index.ts +21 -0
  264. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  265. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  266. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  267. package/tests/cypress/src/controllers/MediaAPIController.js +231 -0
  268. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  269. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  270. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  271. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  272. package/tests/cypress/src/controllers/index.js +25 -0
  273. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  274. package/tests/cypress/src/core/BasePOM.ts +33 -0
  275. package/tests/cypress/src/core/BlockEditorBasePOM.ts +874 -0
  276. package/tests/cypress/src/core/DashboardEntityPOM.ts +41 -0
  277. package/tests/cypress/src/core/index.ts +14 -0
  278. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  279. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  280. package/tests/cypress/src/entities/PatternsPOM.ts +329 -0
  281. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  282. package/tests/cypress/src/entities/TasksPOM.ts +246 -0
  283. package/tests/cypress/src/entities/index.ts +16 -0
  284. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  285. package/tests/cypress/src/features/DashboardPOM.ts +271 -0
  286. package/tests/cypress/src/features/DevtoolsPOM.ts +750 -0
  287. package/tests/cypress/src/features/PageBuilderPOM.ts +283 -0
  288. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  289. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  290. package/tests/cypress/src/features/SettingsPOM.ts +707 -0
  291. package/tests/cypress/src/features/SuperadminPOM.ts +851 -0
  292. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  293. package/tests/cypress/src/features/index.ts +28 -0
  294. package/tests/cypress/src/helpers/ApiInterceptor.ts +20 -0
  295. package/tests/cypress/src/index.ts +101 -0
  296. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  297. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  298. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  299. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  300. package/tests/cypress/src/pages/index.js +5 -0
  301. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  302. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  303. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  304. package/tests/cypress/src/pages/public/index.js +6 -0
  305. package/tests/cypress/src/selectors.ts +46 -0
  306. package/tests/cypress/src/session-helpers.ts +518 -0
  307. package/tests/cypress/support/doc-commands.ts +260 -0
  308. package/tests/cypress/support/e2e.ts +90 -0
  309. package/tests/cypress.config.ts +178 -0
  310. package/tests/jest/__mocks__/@nextsparkjs/core/components/ui/badge.js +16 -0
  311. package/tests/jest/__mocks__/@nextsparkjs/core/lib/db.js +11 -0
  312. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +160 -0
  313. package/tests/jest/__mocks__/@nextsparkjs/registries/theme-registry.ts +68 -0
  314. package/tests/jest/__mocks__/jose.js +22 -0
  315. package/tests/jest/__mocks__/next/image.js +15 -0
  316. package/tests/jest/__mocks__/next-server.js +56 -0
  317. package/tests/jest/components/post-header.test.tsx +377 -0
  318. package/tests/jest/jest.config.cjs +154 -0
  319. package/tests/jest/langchain/COVERAGE.md +372 -0
  320. package/tests/jest/langchain/guardrails.test.ts +465 -0
  321. package/tests/jest/langchain/streaming.test.ts +370 -0
  322. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  323. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  324. package/tests/jest/langchain/tracer.test.ts +823 -0
  325. package/tests/jest/services/tasks.service.test.ts +707 -0
  326. package/tests/jest/setup.ts +170 -0
  327. package/tests/jest/tsconfig.jest.json +6 -0
  328. package/tests/jest/validation/categories.test.ts +429 -0
  329. package/tests/jest/validation/posts.test.ts +546 -0
  330. package/tests/tsconfig.json +21 -0
  331. /package/docs/{02-features → public/02-features}/01-components.md +0 -0
  332. /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
+ })