@nextsparkjs/theme-default 0.1.0-beta.1 → 0.1.0-beta.101

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 (330) hide show
  1. package/LICENSE +21 -0
  2. package/api/ai/chat/stream/route.ts +4 -1
  3. package/api/ai/orchestrator/route.ts +10 -3
  4. package/api/ai/single-agent/route.ts +10 -3
  5. package/api/ai/usage/route.ts +4 -1
  6. package/blocks/benefits/component.tsx +4 -4
  7. package/blocks/cta-section/component.tsx +4 -4
  8. package/blocks/faq-accordion/component.tsx +2 -2
  9. package/blocks/features-grid/component.tsx +5 -5
  10. package/blocks/hero/component.tsx +2 -2
  11. package/blocks/hero/fields.ts +1 -1
  12. package/blocks/hero-with-form/component.tsx +7 -7
  13. package/blocks/hero-with-form/fields.ts +1 -1
  14. package/blocks/jumbotron/component.tsx +7 -7
  15. package/blocks/jumbotron/fields.ts +1 -1
  16. package/blocks/logo-cloud/component.tsx +6 -6
  17. package/blocks/logo-cloud/fields.ts +1 -1
  18. package/blocks/post-content/component.tsx +2 -2
  19. package/blocks/pricing-table/component.tsx +5 -5
  20. package/blocks/split-content/component.tsx +5 -5
  21. package/blocks/split-content/fields.ts +1 -1
  22. package/blocks/stats-counter/component.tsx +9 -9
  23. package/blocks/testimonials/component.tsx +4 -4
  24. package/blocks/testimonials/fields.ts +1 -1
  25. package/blocks/text-content/component.tsx +12 -10
  26. package/blocks/timeline/component.tsx +12 -12
  27. package/blocks/video-hero/component.tsx +7 -7
  28. package/blocks/video-hero/fields.ts +1 -1
  29. package/components/ai-chat/ChatPanel.tsx +7 -7
  30. package/components/ai-chat/Message.tsx +2 -2
  31. package/components/ai-chat/MessageInput.tsx +3 -3
  32. package/components/ai-chat/MessageList.tsx +3 -3
  33. package/components/ai-chat/TypingIndicator.tsx +2 -2
  34. package/config/app.config.ts +75 -62
  35. package/config/dashboard.config.ts +14 -0
  36. package/config/features.config.ts +10 -0
  37. package/config/permissions.config.ts +26 -1
  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/entities/customers/api/docs.md +107 -0
  48. package/entities/customers/api/presets.ts +80 -0
  49. package/entities/pages/api/docs.md +114 -0
  50. package/entities/pages/api/presets.ts +72 -0
  51. package/entities/posts/api/docs.md +120 -0
  52. package/entities/posts/api/presets.ts +74 -0
  53. package/entities/tasks/api/docs.md +126 -0
  54. package/entities/tasks/api/presets.ts +84 -0
  55. package/lib/selectors.ts +7 -4
  56. package/messages/de/admin.json +45 -0
  57. package/messages/en/admin.json +56 -0
  58. package/messages/en/navigation.json +2 -1
  59. package/messages/es/admin.json +56 -0
  60. package/messages/es/navigation.json +2 -1
  61. package/messages/fr/admin.json +45 -0
  62. package/messages/it/admin.json +45 -0
  63. package/messages/pt/admin.json +45 -0
  64. package/migrations/090_demo_users_teams.sql +11 -11
  65. package/migrations/091_greek_teams_billing.sql +15 -15
  66. package/migrations/093_pages_sample_data.sql +7 -7
  67. package/migrations/098_patterns_sample_data.sql +234 -0
  68. package/package.json +8 -3
  69. package/styles/globals.css +42 -0
  70. package/templates/(public)/blog/[slug]/page.tsx +1 -1
  71. package/templates/(public)/page.tsx +1 -1
  72. package/tests/cypress/e2e/_utils/devtools/access.bdd.md +262 -0
  73. package/tests/cypress/e2e/_utils/devtools/access.cy.ts +171 -0
  74. package/tests/cypress/e2e/_utils/devtools/navigation.bdd.md +261 -0
  75. package/tests/cypress/e2e/_utils/devtools/navigation.cy.ts +157 -0
  76. package/tests/cypress/e2e/_utils/devtools/pages.bdd.md +303 -0
  77. package/tests/cypress/e2e/_utils/devtools/pages.cy.ts +184 -0
  78. package/tests/cypress/e2e/_utils/docs/README.md +215 -0
  79. package/tests/cypress/e2e/_utils/selectors/auth.bdd.md +354 -0
  80. package/tests/cypress/e2e/_utils/selectors/auth.cy.ts +310 -0
  81. package/tests/cypress/e2e/_utils/selectors/billing.bdd.md +276 -0
  82. package/tests/cypress/e2e/_utils/selectors/billing.cy.ts +182 -0
  83. package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +615 -0
  84. package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +783 -0
  85. package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
  86. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.bdd.md +205 -0
  87. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +137 -0
  88. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.bdd.md +147 -0
  89. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +114 -0
  90. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +76 -0
  91. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +68 -0
  92. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +326 -0
  93. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +177 -0
  94. package/tests/cypress/e2e/_utils/selectors/devtools.bdd.md +306 -0
  95. package/tests/cypress/e2e/_utils/selectors/devtools.cy.ts +273 -0
  96. package/tests/cypress/e2e/_utils/selectors/global-search.bdd.md +115 -0
  97. package/tests/cypress/e2e/_utils/selectors/global-search.cy.ts +93 -0
  98. package/tests/cypress/e2e/_utils/selectors/patterns.bdd.md +388 -0
  99. package/tests/cypress/e2e/_utils/selectors/patterns.cy.ts +559 -0
  100. package/tests/cypress/e2e/_utils/selectors/public.cy.ts +112 -0
  101. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.bdd.md +266 -0
  102. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.cy.ts +233 -0
  103. package/tests/cypress/e2e/_utils/selectors/settings-billing.bdd.md +78 -0
  104. package/tests/cypress/e2e/_utils/selectors/settings-billing.cy.ts +108 -0
  105. package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +129 -0
  106. package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +115 -0
  107. package/tests/cypress/e2e/_utils/selectors/settings-password.bdd.md +82 -0
  108. package/tests/cypress/e2e/_utils/selectors/settings-password.cy.ts +74 -0
  109. package/tests/cypress/e2e/_utils/selectors/settings-profile.bdd.md +77 -0
  110. package/tests/cypress/e2e/_utils/selectors/settings-profile.cy.ts +79 -0
  111. package/tests/cypress/e2e/_utils/selectors/settings-teams.bdd.md +130 -0
  112. package/tests/cypress/e2e/_utils/selectors/settings-teams.cy.ts +86 -0
  113. package/tests/cypress/e2e/_utils/selectors/superadmin.bdd.md +261 -0
  114. package/tests/cypress/e2e/_utils/selectors/superadmin.cy.ts +193 -0
  115. package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +593 -0
  116. package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +864 -0
  117. package/tests/cypress/e2e/_utils/selectors/taxonomies.cy.ts +126 -0
  118. package/tests/cypress/e2e/_utils/selectors/teams.bdd.md +278 -0
  119. package/tests/cypress/e2e/_utils/selectors/teams.cy.ts +195 -0
  120. package/tests/cypress/e2e/_utils/superadmin/all-teams.bdd.md +261 -0
  121. package/tests/cypress/e2e/_utils/superadmin/all-teams.cy.ts +177 -0
  122. package/tests/cypress/e2e/_utils/superadmin/all-users.bdd.md +406 -0
  123. package/tests/cypress/e2e/_utils/superadmin/all-users.cy.ts +294 -0
  124. package/tests/cypress/e2e/_utils/superadmin/dashboard.bdd.md +235 -0
  125. package/tests/cypress/e2e/_utils/superadmin/dashboard.cy.ts +149 -0
  126. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.bdd.md +290 -0
  127. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.cy.ts +194 -0
  128. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  129. package/tests/cypress/e2e/ai/chat-api.cy.ts +119 -0
  130. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  131. package/tests/cypress/e2e/api/_core/billing/BillingAPIController.js +319 -0
  132. package/tests/cypress/e2e/api/_core/billing/check-action.cy.ts +326 -0
  133. package/tests/cypress/e2e/api/_core/billing/checkout.cy.ts +358 -0
  134. package/tests/cypress/e2e/api/_core/billing/lifecycle.cy.ts +423 -0
  135. package/tests/cypress/e2e/api/_core/billing/plans/README.md +345 -0
  136. package/tests/cypress/e2e/api/_core/billing/plans/business.cy.ts +412 -0
  137. package/tests/cypress/e2e/api/_core/billing/plans/downgrade.cy.ts +510 -0
  138. package/tests/cypress/e2e/api/_core/billing/plans/fixtures/billing-plans.json +163 -0
  139. package/tests/cypress/e2e/api/_core/billing/plans/free.cy.ts +500 -0
  140. package/tests/cypress/e2e/api/_core/billing/plans/pro.cy.ts +497 -0
  141. package/tests/cypress/e2e/api/_core/billing/plans/starter.cy.ts +342 -0
  142. package/tests/cypress/e2e/api/_core/billing/portal.cy.ts +313 -0
  143. package/tests/cypress/e2e/api/_core/devtools/registries.bdd.md +300 -0
  144. package/tests/cypress/e2e/api/_core/devtools/registries.cy.ts +368 -0
  145. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.bdd.md +375 -0
  146. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.cy.ts +346 -0
  147. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  148. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  149. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.bdd.md +649 -0
  150. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.cy.ts +333 -0
  151. package/tests/cypress/e2e/api/_core/security/security-headers.cy.ts +601 -0
  152. package/tests/cypress/e2e/api/_core/settings/api-keys.crud.cy.ts +923 -0
  153. package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
  154. package/tests/cypress/e2e/api/_core/users/users-crud.cy.ts +469 -0
  155. package/tests/cypress/e2e/api/_core/users/users-metas.cy.ts +913 -0
  156. package/tests/cypress/e2e/api/_core/users/users-security.cy.ts +375 -0
  157. package/tests/cypress/e2e/api/entities/customers/customers-crud.cy.ts +648 -0
  158. package/tests/cypress/e2e/api/entities/customers/customers-metas.cy.ts +839 -0
  159. package/tests/cypress/e2e/api/entities/media/media-crud.cy.ts +600 -0
  160. package/tests/cypress/e2e/api/entities/media/media-role-permissions.cy.ts +617 -0
  161. package/tests/cypress/e2e/api/entities/media/media-team-isolation.cy.ts +464 -0
  162. package/tests/cypress/e2e/api/entities/pages/blocks-scope.cy.ts +396 -0
  163. package/tests/cypress/e2e/api/entities/pages/pages-crud.cy.ts +425 -0
  164. package/tests/cypress/e2e/api/entities/pages/pages-status.cy.ts +335 -0
  165. package/tests/cypress/e2e/api/entities/posts/post-categories-crud.cy.ts +610 -0
  166. package/tests/cypress/e2e/api/entities/posts/posts-crud.cy.ts +709 -0
  167. package/tests/cypress/e2e/api/entities/posts/posts-status.cy.ts +396 -0
  168. package/tests/cypress/e2e/api/entities/tasks/tasks-crud.cy.ts +602 -0
  169. package/tests/cypress/e2e/api/entities/tasks/tasks-metas.cy.ts +878 -0
  170. package/tests/cypress/e2e/patterns/patterns-in-pages.cy.ts +367 -0
  171. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.bdd.md +231 -0
  172. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.cy.ts +144 -0
  173. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.bdd.md +118 -0
  174. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.cy.ts +84 -0
  175. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.bdd.md +288 -0
  176. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.cy.ts +188 -0
  177. package/tests/cypress/e2e/uat/_core/auth/login-logout.bdd.md +160 -0
  178. package/tests/cypress/e2e/uat/_core/auth/login-logout.cy.ts +116 -0
  179. package/tests/cypress/e2e/uat/_core/auth/password-reset.bdd.md +289 -0
  180. package/tests/cypress/e2e/uat/_core/auth/password-reset.cy.ts +200 -0
  181. package/tests/cypress/e2e/uat/_core/auth/registration-control-invitation.cy.ts +176 -0
  182. package/tests/cypress/e2e/uat/_core/auth/registration-control-open.cy.ts +131 -0
  183. package/tests/cypress/e2e/uat/_core/auth/registration-control.cy.ts +140 -0
  184. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.bdd.md +225 -0
  185. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.cy.ts +148 -0
  186. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.bdd.md +251 -0
  187. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.cy.ts +163 -0
  188. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.bdd.md +231 -0
  189. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.cy.ts +141 -0
  190. package/tests/cypress/e2e/uat/_core/billing/extended.bdd.md +273 -0
  191. package/tests/cypress/e2e/uat/_core/billing/extended.cy.ts +209 -0
  192. package/tests/cypress/e2e/uat/_core/billing/feature-gates.bdd.md +407 -0
  193. package/tests/cypress/e2e/uat/_core/billing/feature-gates.cy.ts +307 -0
  194. package/tests/cypress/e2e/uat/_core/billing/page.bdd.md +329 -0
  195. package/tests/cypress/e2e/uat/_core/billing/page.cy.ts +250 -0
  196. package/tests/cypress/e2e/uat/_core/billing/status.bdd.md +190 -0
  197. package/tests/cypress/e2e/uat/_core/billing/status.cy.ts +145 -0
  198. package/tests/cypress/e2e/uat/_core/billing/team-switch.bdd.md +156 -0
  199. package/tests/cypress/e2e/uat/_core/billing/team-switch.cy.ts +122 -0
  200. package/tests/cypress/e2e/uat/_core/billing/usage.bdd.md +218 -0
  201. package/tests/cypress/e2e/uat/_core/billing/usage.cy.ts +176 -0
  202. package/tests/cypress/e2e/uat/_core/blocks/hero.bdd.md +124 -0
  203. package/tests/cypress/e2e/uat/_core/blocks/hero.cy.ts +56 -0
  204. package/tests/cypress/e2e/uat/_core/devtools/api-tester.cy.ts +390 -0
  205. package/tests/cypress/e2e/uat/_core/performance/suspense-loading.cy.ts +134 -0
  206. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.bdd.md +736 -0
  207. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.cy.ts +740 -0
  208. package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
  209. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.bdd.md +553 -0
  210. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.cy.ts +185 -0
  211. package/tests/cypress/e2e/uat/_core/teams/switcher.bdd.md +1151 -0
  212. package/tests/cypress/e2e/uat/_core/teams/switcher.cy.ts +497 -0
  213. package/tests/cypress/e2e/uat/_core/teams/team-switcher.md +198 -0
  214. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  215. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  216. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  217. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  218. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  219. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  220. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  221. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  222. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  223. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  224. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  225. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  226. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  227. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  228. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  229. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  230. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  231. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  232. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  233. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  234. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  235. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  236. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  237. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  238. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  239. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  240. package/tests/cypress/e2e/uat/features/roles/editor-role.bdd.md +552 -0
  241. package/tests/cypress/e2e/uat/features/roles/editor-role.cy.ts +210 -0
  242. package/tests/cypress/e2e/uat/features/roles/member-restrictions.bdd.md +450 -0
  243. package/tests/cypress/e2e/uat/features/roles/member-restrictions.cy.ts +189 -0
  244. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.bdd.md +530 -0
  245. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.cy.ts +247 -0
  246. package/tests/cypress/fixtures/blocks.json +218 -0
  247. package/tests/cypress/fixtures/entities.json +87 -0
  248. package/tests/cypress/fixtures/page-builder.json +21 -0
  249. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  250. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  251. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  252. package/tests/cypress/src/components/EntityForm.ts +375 -0
  253. package/tests/cypress/src/components/EntityList.ts +389 -0
  254. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  255. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  256. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  257. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  258. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  259. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  260. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  261. package/tests/cypress/src/components/index.ts +21 -0
  262. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  263. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  264. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  265. package/tests/cypress/src/controllers/MediaAPIController.js +231 -0
  266. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  267. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  268. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  269. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  270. package/tests/cypress/src/controllers/index.js +25 -0
  271. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  272. package/tests/cypress/src/core/BasePOM.ts +33 -0
  273. package/tests/cypress/src/core/BlockEditorBasePOM.ts +874 -0
  274. package/tests/cypress/src/core/DashboardEntityPOM.ts +41 -0
  275. package/tests/cypress/src/core/index.ts +14 -0
  276. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  277. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  278. package/tests/cypress/src/entities/PatternsPOM.ts +329 -0
  279. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  280. package/tests/cypress/src/entities/TasksPOM.ts +246 -0
  281. package/tests/cypress/src/entities/index.ts +16 -0
  282. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  283. package/tests/cypress/src/features/DashboardPOM.ts +271 -0
  284. package/tests/cypress/src/features/DevtoolsPOM.ts +750 -0
  285. package/tests/cypress/src/features/PageBuilderPOM.ts +283 -0
  286. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  287. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  288. package/tests/cypress/src/features/SettingsPOM.ts +707 -0
  289. package/tests/cypress/src/features/SuperadminPOM.ts +851 -0
  290. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  291. package/tests/cypress/src/features/index.ts +28 -0
  292. package/tests/cypress/src/helpers/ApiInterceptor.ts +20 -0
  293. package/tests/cypress/src/index.ts +101 -0
  294. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  295. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  296. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  297. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  298. package/tests/cypress/src/pages/index.js +5 -0
  299. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  300. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  301. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  302. package/tests/cypress/src/pages/public/index.js +6 -0
  303. package/tests/cypress/src/selectors.ts +46 -0
  304. package/tests/cypress/src/session-helpers.ts +518 -0
  305. package/tests/cypress/support/doc-commands.ts +260 -0
  306. package/tests/cypress/support/e2e.ts +90 -0
  307. package/tests/cypress.config.ts +178 -0
  308. package/tests/jest/__mocks__/@nextsparkjs/core/components/ui/badge.js +16 -0
  309. package/tests/jest/__mocks__/@nextsparkjs/core/lib/db.js +11 -0
  310. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +160 -0
  311. package/tests/jest/__mocks__/@nextsparkjs/registries/theme-registry.ts +68 -0
  312. package/tests/jest/__mocks__/jose.js +22 -0
  313. package/tests/jest/__mocks__/next/image.js +15 -0
  314. package/tests/jest/__mocks__/next-server.js +56 -0
  315. package/tests/jest/components/post-header.test.tsx +377 -0
  316. package/tests/jest/jest.config.cjs +154 -0
  317. package/tests/jest/langchain/COVERAGE.md +372 -0
  318. package/tests/jest/langchain/guardrails.test.ts +465 -0
  319. package/tests/jest/langchain/streaming.test.ts +370 -0
  320. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  321. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  322. package/tests/jest/langchain/tracer.test.ts +823 -0
  323. package/tests/jest/services/tasks.service.test.ts +707 -0
  324. package/tests/jest/setup.ts +170 -0
  325. package/tests/jest/tsconfig.jest.json +6 -0
  326. package/tests/jest/validation/categories.test.ts +429 -0
  327. package/tests/jest/validation/posts.test.ts +546 -0
  328. package/tests/tsconfig.json +21 -0
  329. /package/docs/{02-features → public/02-features}/01-components.md +0 -0
  330. /package/docs/{02-features → public/02-features}/02-styling.md +0 -0
@@ -0,0 +1,464 @@
1
+ /**
2
+ * Media API - Team Isolation Tests
3
+ *
4
+ * Validates that media is properly isolated by team:
5
+ * - Users can only see media from their active team
6
+ * - CRUD operations respect team boundaries
7
+ * - Cross-team access is prevented by membership validation (403)
8
+ * - Duplicate detection is team-scoped
9
+ *
10
+ * Test Cases:
11
+ * - MEDIA_TEAM_001: GET without x-team-id falls back to defaultTeamId
12
+ * - MEDIA_TEAM_002: GET with team A only returns team A media
13
+ * - MEDIA_TEAM_003: GET with non-member team returns 403
14
+ * - MEDIA_TEAM_004: POST upload creates media in correct team
15
+ * - MEDIA_TEAM_005: PATCH cannot modify media from another team (403 - not a member)
16
+ * - MEDIA_TEAM_006: DELETE cannot delete media from another team (403 - not a member)
17
+ * - MEDIA_TEAM_007: GET by ID cannot view media from another team (403 - not a member)
18
+ * - MEDIA_TEAM_008: Check duplicates only searches within active team
19
+ * - MEDIA_TEAM_009: Search with non-member team returns 403
20
+ * - MEDIA_TEAM_010: Pagination with non-member team returns 403
21
+ * - MEDIA_TEAM_009: Type filtering with non-member team returns 403
22
+ */
23
+
24
+ /// <reference types="cypress" />
25
+
26
+ import * as allure from 'allure-cypress'
27
+
28
+ const MediaAPIController = require('../../../../src/controllers/MediaAPIController.js')
29
+
30
+ describe('Media API - Team Isolation', {
31
+ tags: ['@api', '@feat-media-library', '@security', '@team-isolation']
32
+ }, () => {
33
+ // Test constants
34
+ const SUPERADMIN_API_KEY = Cypress.env('SUPERADMIN_API_KEY') || 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
35
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:3010'
36
+
37
+ // Team IDs from sample data
38
+ const TEAM_A_ID = 'team-nextspark-001' // NextSpark Team (system admin team with media)
39
+ const TEAM_B_ID = 'team-alpha-001' // Alpha Tech (different team, no media)
40
+
41
+ // Controller instances
42
+ let mediaAPITeamA: InstanceType<typeof MediaAPIController>
43
+ let mediaAPITeamB: InstanceType<typeof MediaAPIController>
44
+ let mediaAPINoTeam: InstanceType<typeof MediaAPIController>
45
+
46
+ // Track created media for cleanup
47
+ let createdMediaTeamA: string[] = []
48
+ let createdMediaTeamB: string[] = []
49
+
50
+ before(() => {
51
+ // Initialize controllers for different team contexts
52
+ mediaAPITeamA = new MediaAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_A_ID)
53
+ mediaAPITeamB = new MediaAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_B_ID)
54
+ mediaAPINoTeam = new MediaAPIController(BASE_URL, SUPERADMIN_API_KEY, null) // No team context
55
+ })
56
+
57
+ beforeEach(() => {
58
+ allure.epic('API')
59
+ allure.feature('Media Library')
60
+ allure.story('Team Isolation')
61
+ })
62
+
63
+ afterEach(() => {
64
+ // Cleanup created media
65
+ createdMediaTeamA.forEach((id) => {
66
+ mediaAPITeamA.deleteMedia(id)
67
+ })
68
+ createdMediaTeamB.forEach((id) => {
69
+ mediaAPITeamB.deleteMedia(id)
70
+ })
71
+ createdMediaTeamA = []
72
+ createdMediaTeamB = []
73
+ })
74
+
75
+ // ============================================
76
+ // MEDIA_TEAM_001: GET without x-team-id falls back to defaultTeamId
77
+ // ============================================
78
+ describe('Team Context Fallback', () => {
79
+ it('MEDIA_TEAM_001: Should fall back to defaultTeamId when x-team-id header is not provided', { tags: '@smoke' }, () => {
80
+ allure.severity('critical')
81
+
82
+ // Superadmin has defaultTeamId set (team-nextspark-001 via activeTeamId meta)
83
+ // Without x-team-id header, API falls back to user's defaultTeamId
84
+ mediaAPINoTeam.getMedia().then((response: any) => {
85
+ expect(response.status).to.eq(200)
86
+ expect(response.body).to.have.property('success', true)
87
+ expect(response.body.data.data).to.be.an('array')
88
+
89
+ // All returned media should belong to the defaultTeamId (team-nextspark-001)
90
+ response.body.data.data.forEach((media: any) => {
91
+ expect(media.teamId).to.eq(TEAM_A_ID)
92
+ })
93
+
94
+ cy.log('Without x-team-id, API falls back to defaultTeamId correctly')
95
+ })
96
+ })
97
+ })
98
+
99
+ // ============================================
100
+ // MEDIA_TEAM_002 & 003: GET returns only team-specific media
101
+ // ============================================
102
+ describe('Team Isolation - List Media', () => {
103
+ it('MEDIA_TEAM_002: Should return only media from team A when using team A context', { tags: '@smoke' }, () => {
104
+ allure.severity('critical')
105
+
106
+ mediaAPITeamA.getMedia().then((response: any) => {
107
+ expect(response.status).to.eq(200)
108
+ expect(response.body).to.have.property('success', true)
109
+ expect(response.body.data.data).to.be.an('array')
110
+
111
+ // All returned media should belong to team A
112
+ response.body.data.data.forEach((media: any) => {
113
+ expect(media.teamId).to.eq(TEAM_A_ID)
114
+ })
115
+
116
+ cy.log(`Team A has ${response.body.data.data.length} media items (all isolated to team A)`)
117
+ })
118
+ })
119
+
120
+ it('MEDIA_TEAM_003: Should return 403 when requesting media from a team the user is not a member of', { tags: '@smoke' }, () => {
121
+ allure.severity('critical')
122
+
123
+ // SuperAdmin is NOT a member of team-alpha-001, so membership validation rejects with 403
124
+ mediaAPITeamB.getMedia().then((response: any) => {
125
+ expect(response.status).to.eq(403)
126
+ expect(response.body).to.have.property('success', false)
127
+
128
+ cy.log('Non-member team access correctly rejected with 403')
129
+ })
130
+ })
131
+ })
132
+
133
+ // ============================================
134
+ // MEDIA_TEAM_004: POST upload creates media in correct team
135
+ // Note: Skipped because cy.request doesn't handle FormData properly.
136
+ // Upload team isolation is verified via curl in manual testing.
137
+ // ============================================
138
+ describe('Team Isolation - Upload', () => {
139
+ it.skip('MEDIA_TEAM_004: Should create media in the correct team context (team A)', () => {
140
+ allure.severity('critical')
141
+
142
+ const filename = `test-team-a-${Date.now()}.jpg`
143
+ cy.fixture('test-image.jpg', 'binary').then((fileContent) => {
144
+ const blob = Cypress.Blob.binaryStringToBlob(fileContent, 'image/jpeg')
145
+ const file = new File([blob], filename, { type: 'image/jpeg' })
146
+
147
+ mediaAPITeamA.uploadMedia(file).then((response: any) => {
148
+ expect(response.status).to.eq(200)
149
+ expect(response.body).to.have.property('success', true)
150
+ expect(response.body.data.media).to.be.an('array')
151
+ expect(response.body.data.media.length).to.be.greaterThan(0)
152
+
153
+ const uploadedMedia = response.body.data.media[0]
154
+ expect(uploadedMedia.teamId).to.eq(TEAM_A_ID)
155
+ createdMediaTeamA.push(uploadedMedia.id)
156
+
157
+ cy.log(`Media uploaded to team A: ${uploadedMedia.id}`)
158
+
159
+ // Verify team B cannot see this media (403 - not a member)
160
+ mediaAPITeamB.getMediaById(uploadedMedia.id).then((getResponse: any) => {
161
+ expect(getResponse.status).to.eq(403)
162
+ cy.log('Team B cannot access team A media (403 - not a member)')
163
+ })
164
+ })
165
+ })
166
+ })
167
+ })
168
+
169
+ // ============================================
170
+ // MEDIA_TEAM_005: PATCH cannot modify media from another team
171
+ // ============================================
172
+ describe('Team Isolation - Update', () => {
173
+ it('MEDIA_TEAM_005: Should return 403 when trying to update media from a non-member team', () => {
174
+ allure.severity('critical')
175
+
176
+ // Get first media from team A
177
+ mediaAPITeamA.getMedia({ limit: 1 }).then((listResponse: any) => {
178
+ expect(listResponse.body.data.data.length).to.be.greaterThan(0)
179
+
180
+ const teamAMediaId = listResponse.body.data.data[0].id
181
+
182
+ // Try to update it using team B context (user is not a member of team B)
183
+ const updateData = { alt: 'Hacked from team B' }
184
+
185
+ mediaAPITeamB.updateMedia(teamAMediaId, updateData).then((updateResponse: any) => {
186
+ // Membership validation rejects before media lookup
187
+ expect(updateResponse.status).to.eq(403)
188
+ expect(updateResponse.body).to.have.property('success', false)
189
+
190
+ cy.log('Non-member team cannot update media (403)')
191
+
192
+ // Verify original media unchanged
193
+ mediaAPITeamA.getMediaById(teamAMediaId).then((getResponse: any) => {
194
+ expect(getResponse.status).to.eq(200)
195
+ expect(getResponse.body.data.alt).to.not.eq('Hacked from team B')
196
+ cy.log('Team A media unchanged after non-member update attempt')
197
+ })
198
+ })
199
+ })
200
+ })
201
+ })
202
+
203
+ // ============================================
204
+ // MEDIA_TEAM_006: DELETE cannot delete media from another team
205
+ // ============================================
206
+ describe('Team Isolation - Delete', () => {
207
+ it('MEDIA_TEAM_006: Should return 403 when trying to delete media from a non-member team', () => {
208
+ allure.severity('critical')
209
+
210
+ // Get first media from team A
211
+ mediaAPITeamA.getMedia({ limit: 1 }).then((listResponse: any) => {
212
+ expect(listResponse.body.data.data.length).to.be.greaterThan(0)
213
+
214
+ const teamAMediaId = listResponse.body.data.data[0].id
215
+
216
+ // Try to delete it using team B context (user is not a member of team B)
217
+ mediaAPITeamB.deleteMedia(teamAMediaId).then((deleteResponse: any) => {
218
+ // Membership validation rejects before media lookup
219
+ expect(deleteResponse.status).to.eq(403)
220
+ expect(deleteResponse.body).to.have.property('success', false)
221
+
222
+ cy.log('Non-member team cannot delete media (403)')
223
+
224
+ // Verify media still exists for team A
225
+ mediaAPITeamA.getMediaById(teamAMediaId).then((getResponse: any) => {
226
+ expect(getResponse.status).to.eq(200)
227
+ cy.log('Team A media still exists after non-member delete attempt')
228
+ })
229
+ })
230
+ })
231
+ })
232
+ })
233
+
234
+ // ============================================
235
+ // MEDIA_TEAM_007: GET by ID cannot view media from another team
236
+ // ============================================
237
+ describe('Team Isolation - Get by ID', () => {
238
+ it('MEDIA_TEAM_007: Should return 403 when trying to access media from a non-member team by ID', { tags: '@smoke' }, () => {
239
+ allure.severity('critical')
240
+
241
+ // Get first media from team A
242
+ mediaAPITeamA.getMedia({ limit: 1 }).then((listResponse: any) => {
243
+ expect(listResponse.body.data.data.length).to.be.greaterThan(0)
244
+
245
+ const teamAMediaId = listResponse.body.data.data[0].id
246
+
247
+ // Try to access it using team B context (user is not a member of team B)
248
+ mediaAPITeamB.getMediaById(teamAMediaId).then((getResponse: any) => {
249
+ // Membership validation rejects before media lookup
250
+ expect(getResponse.status).to.eq(403)
251
+ expect(getResponse.body).to.have.property('success', false)
252
+
253
+ cy.log('Non-member team cannot view media by ID (403)')
254
+ })
255
+ })
256
+ })
257
+ })
258
+
259
+ // ============================================
260
+ // MEDIA_TEAM_008: Check duplicates only searches within active team
261
+ // ============================================
262
+ describe('Team Isolation - Duplicate Detection', () => {
263
+ it.skip('MEDIA_TEAM_008: Should only detect duplicates within the same team', () => {
264
+ allure.severity('high')
265
+
266
+ const filename = `duplicate-test-${Date.now()}.jpg`
267
+
268
+ // Upload to team A
269
+ cy.fixture('test-image.jpg', 'binary').then((fileContent) => {
270
+ const blob = Cypress.Blob.binaryStringToBlob(fileContent, 'image/jpeg')
271
+ const fileA = new File([blob], filename, { type: 'image/jpeg' })
272
+
273
+ mediaAPITeamA.uploadMedia(fileA).then((uploadAResponse: any) => {
274
+ expect(uploadAResponse.status).to.eq(200)
275
+ const mediaA = uploadAResponse.body.data.media[0]
276
+ createdMediaTeamA.push(mediaA.id)
277
+
278
+ cy.log(`Uploaded to team A: ${mediaA.id}`)
279
+
280
+ // Upload same file to team B (should NOT be detected as duplicate)
281
+ const fileB = new File([blob], filename, { type: 'image/jpeg' })
282
+
283
+ mediaAPITeamB.uploadMedia(fileB).then((uploadBResponse: any) => {
284
+ expect(uploadBResponse.status).to.eq(200)
285
+ const mediaB = uploadBResponse.body.data.media[0]
286
+ createdMediaTeamB.push(mediaB.id)
287
+
288
+ cy.log(`Uploaded to team B: ${mediaB.id}`)
289
+
290
+ // Both uploads should succeed (no duplicate detected across teams)
291
+ expect(mediaA.id).to.not.eq(mediaB.id)
292
+ expect(mediaA.teamId).to.eq(TEAM_A_ID)
293
+ expect(mediaB.teamId).to.eq(TEAM_B_ID)
294
+
295
+ cy.log('Same file uploaded to both teams - no cross-team duplicate detection')
296
+ })
297
+ })
298
+ })
299
+ })
300
+ })
301
+
302
+ // ============================================
303
+ // MEDIA_TEAM_009: Search respects team isolation
304
+ // ============================================
305
+ describe('Team Isolation - Search', () => {
306
+ it('MEDIA_TEAM_009: Should search within active team and reject non-member teams', () => {
307
+ allure.severity('high')
308
+
309
+ const searchTerm = 'sample'
310
+
311
+ // Search in team A (user is a member)
312
+ mediaAPITeamA.getMedia({ search: searchTerm }).then((searchAResponse: any) => {
313
+ expect(searchAResponse.status).to.eq(200)
314
+ expect(searchAResponse.body).to.have.property('success', true)
315
+ expect(searchAResponse.body.data.data).to.be.an('array')
316
+
317
+ // All results should belong to team A
318
+ searchAResponse.body.data.data.forEach((media: any) => {
319
+ expect(media.teamId).to.eq(TEAM_A_ID)
320
+ })
321
+
322
+ cy.log(`Team A search found ${searchAResponse.body.data.data.length} results (all in team A)`)
323
+ })
324
+
325
+ // Search in team B (user is NOT a member) - should be rejected
326
+ mediaAPITeamB.getMedia({ search: searchTerm }).then((searchBResponse: any) => {
327
+ expect(searchBResponse.status).to.eq(403)
328
+ expect(searchBResponse.body).to.have.property('success', false)
329
+
330
+ cy.log('Non-member team search correctly rejected with 403')
331
+ })
332
+ })
333
+ })
334
+
335
+ // ============================================
336
+ // MEDIA_TEAM_010: Pagination reflects only active team
337
+ // ============================================
338
+ describe('Team Isolation - Pagination', () => {
339
+ it('MEDIA_TEAM_010: Should paginate within member team and reject non-member teams', () => {
340
+ allure.severity('medium')
341
+
342
+ // Get total count for team A (user is a member)
343
+ mediaAPITeamA.getMedia({ limit: 100 }).then((teamAResponse: any) => {
344
+ const teamATotal = teamAResponse.body.data.total
345
+
346
+ expect(teamAResponse.body.data.data).to.be.an('array')
347
+ expect(teamATotal).to.be.greaterThan(0)
348
+
349
+ teamAResponse.body.data.data.forEach((media: any) => {
350
+ expect(media.teamId).to.eq(TEAM_A_ID)
351
+ })
352
+
353
+ cy.log(`Team A total: ${teamATotal} (correctly isolated)`)
354
+
355
+ // Team B access (user is NOT a member) - should be rejected
356
+ mediaAPITeamB.getMedia({ limit: 100 }).then((teamBResponse: any) => {
357
+ expect(teamBResponse.status).to.eq(403)
358
+ expect(teamBResponse.body).to.have.property('success', false)
359
+
360
+ cy.log('Non-member team pagination correctly rejected with 403')
361
+ })
362
+ })
363
+ })
364
+ })
365
+
366
+ // ============================================
367
+ // MEDIA_TEAM_011: Type filtering respects team isolation
368
+ // ============================================
369
+ describe('Team Isolation - Type Filtering', () => {
370
+ it('MEDIA_TEAM_011: Should filter by type within member team and reject non-member teams', () => {
371
+ allure.severity('medium')
372
+
373
+ // Get images from team A (user is a member)
374
+ mediaAPITeamA.getMedia({ type: 'image' }).then((teamAResponse: any) => {
375
+ expect(teamAResponse.status).to.eq(200)
376
+ expect(teamAResponse.body).to.have.property('success', true)
377
+ expect(teamAResponse.body.data.data).to.be.an('array')
378
+
379
+ // All results should be images from team A
380
+ teamAResponse.body.data.data.forEach((media: any) => {
381
+ expect(media.teamId).to.eq(TEAM_A_ID)
382
+ })
383
+
384
+ cy.log(`Team A has ${teamAResponse.body.data.data.length} images (all in team A)`)
385
+ })
386
+
387
+ // Get images from team B (user is NOT a member) - should be rejected
388
+ mediaAPITeamB.getMedia({ type: 'image' }).then((teamBResponse: any) => {
389
+ expect(teamBResponse.status).to.eq(403)
390
+ expect(teamBResponse.body).to.have.property('success', false)
391
+
392
+ cy.log('Non-member team type filter correctly rejected with 403')
393
+ })
394
+ })
395
+ })
396
+
397
+ // ============================================
398
+ // Integration Test: Full CRUD lifecycle with team isolation
399
+ // ============================================
400
+ describe('Integration - CRUD Lifecycle with Team Isolation', () => {
401
+ it.skip('MEDIA_TEAM_100: Should complete full lifecycle respecting team boundaries', () => {
402
+ allure.severity('critical')
403
+
404
+ const filename = `lifecycle-test-${Date.now()}.jpg`
405
+
406
+ // 1. Upload to team A
407
+ cy.fixture('test-image.jpg', 'binary').then((fileContent) => {
408
+ const blob = Cypress.Blob.binaryStringToBlob(fileContent, 'image/jpeg')
409
+ const file = new File([blob], filename, { type: 'image/jpeg' })
410
+
411
+ mediaAPITeamA.uploadMedia(file).then((uploadResponse: any) => {
412
+ expect(uploadResponse.status).to.eq(200)
413
+ const mediaId = uploadResponse.body.data.media[0].id
414
+ createdMediaTeamA.push(mediaId)
415
+
416
+ cy.log(`1. Uploaded to team A: ${mediaId}`)
417
+
418
+ // 2. Team A can read it
419
+ mediaAPITeamA.getMediaById(mediaId).then((readResponse: any) => {
420
+ expect(readResponse.status).to.eq(200)
421
+ expect(readResponse.body.data.teamId).to.eq(TEAM_A_ID)
422
+ cy.log('2. Team A can read the media')
423
+
424
+ // 3. Team B CANNOT read it (403 - not a member)
425
+ mediaAPITeamB.getMediaById(mediaId).then((crossTeamReadResponse: any) => {
426
+ expect(crossTeamReadResponse.status).to.eq(403)
427
+ cy.log('3. Team B cannot read team A media (403 - not a member)')
428
+
429
+ // 4. Team A can update it
430
+ mediaAPITeamA.updateMedia(mediaId, { alt: 'Updated by team A' }).then((updateResponse: any) => {
431
+ expect(updateResponse.status).to.eq(200)
432
+ expect(updateResponse.body.data.alt).to.eq('Updated by team A')
433
+ cy.log('4. Team A updated the media')
434
+
435
+ // 5. Team B CANNOT update it (403 - not a member)
436
+ mediaAPITeamB.updateMedia(mediaId, { alt: 'Hacked by team B' }).then((crossTeamUpdateResponse: any) => {
437
+ expect(crossTeamUpdateResponse.status).to.eq(403)
438
+ cy.log('5. Team B cannot update team A media (403 - not a member)')
439
+
440
+ // 6. Team A can delete it
441
+ mediaAPITeamA.deleteMedia(mediaId).then((deleteResponse: any) => {
442
+ expect(deleteResponse.status).to.eq(200)
443
+ cy.log('6. Team A deleted the media')
444
+
445
+ // 7. Verify deletion (404 for both teams)
446
+ mediaAPITeamA.getMediaById(mediaId).then((verifyAResponse: any) => {
447
+ expect(verifyAResponse.status).to.eq(404)
448
+
449
+ mediaAPITeamB.getMediaById(mediaId).then((verifyBResponse: any) => {
450
+ expect(verifyBResponse.status).to.eq(403)
451
+ cy.log('7. Media deleted - team A gets 404, team B gets 403 (not a member)')
452
+ cy.log('Full lifecycle completed with team isolation!')
453
+ })
454
+ })
455
+ })
456
+ })
457
+ })
458
+ })
459
+ })
460
+ })
461
+ })
462
+ })
463
+ })
464
+ })