@nextsparkjs/theme-default 0.1.0-beta.17 → 0.1.0-beta.170

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,783 @@
1
+ /**
2
+ * Block Editor Selectors Validation Test
3
+ *
4
+ * Comprehensive test suite validating ALL selectors in BlockEditorBasePOM.
5
+ * Uses the `pages` entity as the test base.
6
+ *
7
+ * Test Structure:
8
+ * - SEL_BE_001: Header Selectors
9
+ * - SEL_BE_002: Block Picker Selectors
10
+ * - SEL_BE_003: Entity Fields Panel Selectors
11
+ * - SEL_BE_004: Layout Canvas Selectors
12
+ * - SEL_BE_005: Preview Canvas Selectors
13
+ * - SEL_BE_006: Block Properties Panel Selectors
14
+ * - SEL_BE_007: Array Fields Selectors
15
+ * - SEL_BE_008: Patterns Tab Selectors
16
+ * - SEL_BE_009: Pattern Reference Selectors
17
+ *
18
+ * Re-execution:
19
+ * pnpm tags @SEL_BE_001 # Run only Header tests
20
+ * pnpm tags @SEL_BE_004 # Run only Layout Canvas tests
21
+ * pnpm tags @SEL_BE_008 # Run only Patterns Tab tests
22
+ * pnpm tags @patterns # Run all patterns-related tests
23
+ * pnpm tags @ui-selectors # Run all selector tests
24
+ */
25
+
26
+ import { PageBuilderPOM } from '../../../src/features/PageBuilderPOM'
27
+ import { PagesPOM } from '../../../src/entities/PagesPOM'
28
+ import { loginAsDefaultDeveloper } from '../../../src/session-helpers'
29
+
30
+ const DEVELOPER_TEAM_ID = 'team-nextspark-001'
31
+
32
+ describe('Block Editor Selectors Validation', {
33
+ tags: ['@ui-selectors', '@block-editor', '@selector-validation']
34
+ }, () => {
35
+ const pom = PageBuilderPOM.create()
36
+ const pages = PagesPOM.create()
37
+
38
+ beforeEach(() => {
39
+ loginAsDefaultDeveloper()
40
+ cy.window().then((win) => {
41
+ win.localStorage.setItem('activeTeamId', DEVELOPER_TEAM_ID)
42
+ })
43
+ })
44
+
45
+ // ===========================================================================
46
+ // SEL_BE_001: HEADER SELECTORS
47
+ // ===========================================================================
48
+ describe('SEL_BE_001: Header Selectors', { tags: '@SEL_BE_001' }, () => {
49
+ beforeEach(() => {
50
+ pom.visitCreate()
51
+ pom.waitForEditor()
52
+ })
53
+
54
+ it('SEL_BE_001_01: should find header container', { tags: '@SEL_BE_001_01' }, () => {
55
+ cy.get(pom.editorSelectors.container).should('exist').and('be.visible')
56
+ })
57
+
58
+ it('SEL_BE_001_02: should find back button', { tags: '@SEL_BE_001_02' }, () => {
59
+ cy.get(pom.editorSelectors.backButton).should('exist').and('be.visible')
60
+ })
61
+
62
+ it('SEL_BE_001_03: should find title input', { tags: '@SEL_BE_001_03' }, () => {
63
+ cy.get(pom.editorSelectors.titleInput).should('exist').and('be.visible')
64
+ })
65
+
66
+ it('SEL_BE_001_04: should find slug input', { tags: '@SEL_BE_001_04' }, () => {
67
+ cy.get(pom.editorSelectors.slugInput).should('exist').and('be.visible')
68
+ })
69
+
70
+ it('SEL_BE_001_05: should find view mode toggle', { tags: '@SEL_BE_001_05' }, () => {
71
+ cy.get(pom.editorSelectors.viewModeToggle).should('exist').and('be.visible')
72
+ })
73
+
74
+ it('SEL_BE_001_06: should find settings mode button (viewSettings)', { tags: '@SEL_BE_001_06' }, () => {
75
+ // v2.0: viewEditor renamed to viewSettings
76
+ cy.get(pom.editorSelectors.viewSettings).should('exist').and('be.visible')
77
+ })
78
+
79
+ it('SEL_BE_001_07: should find preview mode button (viewPreview)', { tags: '@SEL_BE_001_07' }, () => {
80
+ cy.get(pom.editorSelectors.viewPreview).should('exist').and('be.visible')
81
+ })
82
+
83
+ it('SEL_BE_001_08: should find save button', { tags: '@SEL_BE_001_08' }, () => {
84
+ cy.get(pom.editorSelectors.saveButton).should('exist').and('be.visible')
85
+ })
86
+
87
+ it('SEL_BE_001_09: should find publish button', { tags: '@SEL_BE_001_09' }, () => {
88
+ cy.get(pom.editorSelectors.publishButton).should('exist').and('be.visible')
89
+ })
90
+
91
+ it('SEL_BE_001_10: should find status dot', { tags: '@SEL_BE_001_10' }, () => {
92
+ cy.get(pom.editorSelectors.statusDot).should('exist').and('be.visible')
93
+ })
94
+
95
+ it('SEL_BE_001_11: should find status label', { tags: '@SEL_BE_001_11' }, () => {
96
+ cy.get(pom.editorSelectors.statusLabel).should('exist').and('be.visible')
97
+ })
98
+ })
99
+
100
+ // ===========================================================================
101
+ // SEL_BE_002: BLOCK PICKER SELECTORS
102
+ // ===========================================================================
103
+ describe('SEL_BE_002: Block Picker Selectors', { tags: '@SEL_BE_002' }, () => {
104
+ beforeEach(() => {
105
+ pom.visitCreate()
106
+ pom.waitForEditor()
107
+ })
108
+
109
+ it('SEL_BE_002_01: should find block picker container', { tags: '@SEL_BE_002_01' }, () => {
110
+ cy.get(pom.editorSelectors.blockPicker).should('exist').and('be.visible')
111
+ })
112
+
113
+ it('SEL_BE_002_02: should find blocks tab', { tags: '@SEL_BE_002_02' }, () => {
114
+ cy.get(pom.editorSelectors.tabBlocks).should('exist').and('be.visible')
115
+ })
116
+
117
+ it('SEL_BE_002_03: should find layout tab', { tags: '@SEL_BE_002_03' }, () => {
118
+ // v2.0: tabConfig replaced by tabLayout (tree view)
119
+ // Entity fields moved to Settings mode in center column
120
+ cy.get(pom.editorSelectors.tabLayout).should('exist').and('be.visible')
121
+ })
122
+
123
+ it('SEL_BE_002_04: should find search input', { tags: '@SEL_BE_002_04' }, () => {
124
+ cy.get(pom.editorSelectors.blockSearch).should('exist').and('be.visible')
125
+ })
126
+
127
+ it('SEL_BE_002_05: should find category chips container', { tags: '@SEL_BE_002_05' }, () => {
128
+ cy.get(pom.editorSelectors.categoryChips).should('exist').and('be.visible')
129
+ })
130
+
131
+ it('SEL_BE_002_06: should find category chip by name', { tags: '@SEL_BE_002_06' }, () => {
132
+ // Test with 'content' or 'hero' category depending on available blocks
133
+ cy.get(pom.editorSelectors.categoryGeneric).first().should('exist').and('be.visible')
134
+ })
135
+
136
+ it('SEL_BE_002_07: should find hero block card', { tags: '@SEL_BE_002_07' }, () => {
137
+ cy.get(pom.editorSelectors.blockItem('hero')).scrollIntoView().should('exist').and('be.visible')
138
+ })
139
+
140
+ it('SEL_BE_002_08: should find hero add button', { tags: '@SEL_BE_002_08' }, () => {
141
+ // Hover to show add button
142
+ cy.get(pom.editorSelectors.blockItem('hero')).trigger('mouseenter')
143
+ cy.get(pom.editorSelectors.addBlock('hero')).should('exist')
144
+ })
145
+ })
146
+
147
+ // ===========================================================================
148
+ // SEL_BE_003: ENTITY FIELDS PANEL SELECTORS
149
+ // ===========================================================================
150
+ describe('SEL_BE_003: Entity Fields Panel Selectors', { tags: '@SEL_BE_003' }, () => {
151
+ let testPageId: string
152
+
153
+ before(() => {
154
+ // Create a test page to ensure we have one to edit
155
+ loginAsDefaultDeveloper()
156
+ cy.request({
157
+ method: 'POST',
158
+ url: '/api/v1/pages',
159
+ headers: {
160
+ 'x-team-id': DEVELOPER_TEAM_ID,
161
+ 'Content-Type': 'application/json'
162
+ },
163
+ body: {
164
+ title: `Selector Test Page ${Date.now()}`,
165
+ slug: `selector-test-${Date.now()}`,
166
+ locale: 'en',
167
+ published: false,
168
+ blocks: []
169
+ }
170
+ }).then((response) => {
171
+ testPageId = response.body.data.id
172
+ })
173
+ })
174
+
175
+ beforeEach(() => {
176
+ pom.visitEdit(testPageId)
177
+ pom.waitForEditor()
178
+ // v2.0: Switch to Settings mode to see entity fields (moved to center column)
179
+ cy.get(pom.editorSelectors.viewSettings).click()
180
+ })
181
+
182
+ after(() => {
183
+ // Cleanup test page
184
+ if (testPageId) {
185
+ cy.request({
186
+ method: 'DELETE',
187
+ url: `/api/v1/pages/${testPageId}`,
188
+ headers: { 'x-team-id': DEVELOPER_TEAM_ID },
189
+ failOnStatusCode: false
190
+ })
191
+ }
192
+ })
193
+
194
+ it('SEL_BE_003_01: should find entity fields section container', { tags: '@SEL_BE_003_01' }, () => {
195
+ // v2.0: Entity fields moved to configPanel.entityFieldsSection in Settings mode
196
+ cy.get(pom.editorSelectors.configEntitySection).should('exist').and('be.visible')
197
+ })
198
+
199
+ // Note: These tests depend on the entity having specific fields configured
200
+ it('SEL_BE_003_02: should find entity field by name', { tags: '@SEL_BE_003_02' }, () => {
201
+ // v2.0: Selector pattern is builder-config-entity-field-{name}
202
+ cy.get('[data-cy^="builder-config-entity-field-"]').should('exist')
203
+ })
204
+
205
+ it('SEL_BE_003_03: should find category list (if taxonomies enabled)', { tags: '@SEL_BE_003_03' }, () => {
206
+ // This test is conditional - taxonomies may or may not be enabled
207
+ cy.get('body').then(($body) => {
208
+ if ($body.find(pom.editorSelectors.entityCategoryList).length > 0) {
209
+ cy.get(pom.editorSelectors.entityCategoryList).should('exist')
210
+ } else {
211
+ cy.log('Taxonomies not enabled for pages - skipping')
212
+ }
213
+ })
214
+ })
215
+ })
216
+
217
+ // ===========================================================================
218
+ // SEL_BE_004: TREE VIEW SELECTORS (v2.0 - Layout tab)
219
+ // NOTE: layoutCanvas is deprecated in v2.0. Block list is now in treeView (Layout tab).
220
+ // ===========================================================================
221
+ describe('SEL_BE_004: Tree View Selectors (Layout Tab)', { tags: '@SEL_BE_004' }, () => {
222
+ beforeEach(() => {
223
+ pom.visitCreate()
224
+ pom.waitForEditor()
225
+ // v2.0: Click Layout tab in left sidebar to see tree view
226
+ cy.get(pom.editorSelectors.tabLayout).click()
227
+ })
228
+
229
+ // When no blocks: empty state in tree view
230
+ it('SEL_BE_004_01: should find empty state when no blocks', { tags: '@SEL_BE_004_01' }, () => {
231
+ cy.get(pom.editorSelectors.treeViewEmpty).should('exist').and('be.visible')
232
+ })
233
+
234
+ describe('With block added', () => {
235
+ beforeEach(() => {
236
+ // Switch to Blocks tab to add a block
237
+ cy.get(pom.editorSelectors.tabBlocks).click()
238
+ pom.addBlock('hero')
239
+ // Switch back to Layout tab
240
+ cy.get(pom.editorSelectors.tabLayout).click()
241
+ // Wait for tree view to show the block
242
+ cy.get(pom.editorSelectors.treeView).should('exist')
243
+ })
244
+
245
+ // Tree view container exists when there ARE blocks
246
+ it('SEL_BE_004_02: should find tree view container with blocks', { tags: '@SEL_BE_004_02' }, () => {
247
+ cy.get(pom.editorSelectors.treeView).should('exist').and('be.visible')
248
+ })
249
+
250
+ // Test preview canvas has blocks (v2.0: blocks render in preview mode)
251
+ it('SEL_BE_004_03: should find preview blocks (generic)', { tags: '@SEL_BE_004_03' }, () => {
252
+ // Preview canvas is visible by default in v2.0
253
+ cy.get(pom.editorSelectors.previewBlockGeneric).should('exist')
254
+ })
255
+
256
+ it('SEL_BE_004_04: should find specific preview block by ID', { tags: '@SEL_BE_004_04' }, () => {
257
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
258
+ .invoke('attr', 'data-cy')
259
+ .then((dataCy) => {
260
+ const blockId = dataCy?.replace('preview-block-', '') || ''
261
+ expect(blockId).to.not.be.empty
262
+ cy.get(pom.editorSelectors.previewBlock(blockId)).should('exist')
263
+ })
264
+ })
265
+
266
+ it('SEL_BE_004_05: should find floating toolbar drag handle on hover', { tags: '@SEL_BE_004_05' }, () => {
267
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
268
+ .invoke('attr', 'data-cy')
269
+ .then((dataCy) => {
270
+ const blockId = dataCy?.replace('preview-block-', '') || ''
271
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
272
+ cy.get(pom.editorSelectors.floatingToolbarDrag(blockId)).should('exist')
273
+ })
274
+ })
275
+
276
+ it('SEL_BE_004_06: should find floating toolbar duplicate button', { tags: '@SEL_BE_004_06' }, () => {
277
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
278
+ .invoke('attr', 'data-cy')
279
+ .then((dataCy) => {
280
+ const blockId = dataCy?.replace('preview-block-', '') || ''
281
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
282
+ cy.get(pom.editorSelectors.floatingToolbarDuplicate(blockId)).should('exist')
283
+ })
284
+ })
285
+
286
+ it('SEL_BE_004_07: should find floating toolbar delete button', { tags: '@SEL_BE_004_07' }, () => {
287
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
288
+ .invoke('attr', 'data-cy')
289
+ .then((dataCy) => {
290
+ const blockId = dataCy?.replace('preview-block-', '') || ''
291
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
292
+ cy.get(pom.editorSelectors.floatingToolbarDelete(blockId)).should('exist')
293
+ })
294
+ })
295
+
296
+ it('SEL_BE_004_08: should display block name in floating toolbar', { tags: '@SEL_BE_004_08' }, () => {
297
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
298
+ .invoke('attr', 'data-cy')
299
+ .then((dataCy) => {
300
+ const blockId = dataCy?.replace('preview-block-', '') || ''
301
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
302
+ cy.get(pom.editorSelectors.floatingToolbarName(blockId)).should('contain.text', 'Hero')
303
+ })
304
+ })
305
+ })
306
+ })
307
+
308
+ // ===========================================================================
309
+ // SEL_BE_005: PREVIEW CANVAS SELECTORS
310
+ // ===========================================================================
311
+ describe('SEL_BE_005: Preview Canvas Selectors', { tags: '@SEL_BE_005' }, () => {
312
+ beforeEach(() => {
313
+ pom.visitCreate()
314
+ pom.waitForEditor()
315
+ // v2.0: Preview mode is the default, just add a block
316
+ pom.addBlock('hero')
317
+ // Wait for block to render in preview canvas
318
+ cy.get(pom.editorSelectors.previewBlockGeneric).should('have.length.at.least', 1)
319
+ })
320
+
321
+ it('SEL_BE_005_01: should find preview canvas container', { tags: '@SEL_BE_005_01' }, () => {
322
+ cy.get(pom.editorSelectors.previewCanvas).should('exist').and('be.visible')
323
+ })
324
+
325
+ it('SEL_BE_005_02: should find preview blocks (generic)', { tags: '@SEL_BE_005_02' }, () => {
326
+ cy.get(pom.editorSelectors.previewBlockGeneric).should('exist')
327
+ })
328
+
329
+ it('SEL_BE_005_03: should find specific preview block by ID', { tags: '@SEL_BE_005_03' }, () => {
330
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
331
+ .invoke('attr', 'data-cy')
332
+ .then((dataCy) => {
333
+ const blockId = dataCy?.replace('preview-block-', '') || ''
334
+ expect(blockId).to.not.be.empty
335
+ cy.get(pom.editorSelectors.previewBlock(blockId)).should('exist')
336
+ })
337
+ })
338
+
339
+ it('SEL_BE_005_04: should find floating toolbar on hover', { tags: '@SEL_BE_005_04' }, () => {
340
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
341
+ .invoke('attr', 'data-cy')
342
+ .then((dataCy) => {
343
+ const blockId = dataCy?.replace('preview-block-', '') || ''
344
+ // Hover to show toolbar
345
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
346
+ cy.get(pom.editorSelectors.floatingToolbar(blockId)).should('exist')
347
+ })
348
+ })
349
+
350
+ it('SEL_BE_005_05: should find toolbar block name', { tags: '@SEL_BE_005_05' }, () => {
351
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
352
+ .invoke('attr', 'data-cy')
353
+ .then((dataCy) => {
354
+ const blockId = dataCy?.replace('preview-block-', '') || ''
355
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
356
+ cy.get(pom.editorSelectors.floatingToolbarName(blockId)).should('exist')
357
+ })
358
+ })
359
+
360
+ it('SEL_BE_005_06: should find toolbar duplicate button', { tags: '@SEL_BE_005_06' }, () => {
361
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
362
+ .invoke('attr', 'data-cy')
363
+ .then((dataCy) => {
364
+ const blockId = dataCy?.replace('preview-block-', '') || ''
365
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
366
+ cy.get(pom.editorSelectors.floatingToolbarDuplicate(blockId)).should('exist')
367
+ })
368
+ })
369
+
370
+ it('SEL_BE_005_07: should find toolbar delete button', { tags: '@SEL_BE_005_07' }, () => {
371
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
372
+ .invoke('attr', 'data-cy')
373
+ .then((dataCy) => {
374
+ const blockId = dataCy?.replace('preview-block-', '') || ''
375
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
376
+ cy.get(pom.editorSelectors.floatingToolbarDelete(blockId)).should('exist')
377
+ })
378
+ })
379
+ })
380
+
381
+ // ===========================================================================
382
+ // SEL_BE_006: BLOCK PROPERTIES PANEL SELECTORS
383
+ // ===========================================================================
384
+ describe('SEL_BE_006: Block Properties Panel Selectors', { tags: '@SEL_BE_006' }, () => {
385
+ beforeEach(() => {
386
+ pom.visitCreate()
387
+ pom.waitForEditor()
388
+ })
389
+
390
+ // When no block selected: only empty state exists (no container)
391
+ it('SEL_BE_006_01: should find empty state when no block selected', { tags: '@SEL_BE_006_01' }, () => {
392
+ cy.get(pom.editorSelectors.blockPropertiesEmpty).should('exist').and('be.visible')
393
+ })
394
+
395
+ describe('With block selected', () => {
396
+ beforeEach(() => {
397
+ // v2.0: Add block (auto-selected) in Preview mode
398
+ pom.addBlock('hero')
399
+ // Block is auto-selected after adding - wait for it in preview canvas
400
+ cy.get(pom.editorSelectors.previewBlockGeneric).should('have.length.at.least', 1)
401
+ })
402
+
403
+ // Container only exists when a block IS selected
404
+ it('SEL_BE_006_02: should find properties panel container with block selected', { tags: '@SEL_BE_006_02' }, () => {
405
+ cy.get(pom.editorSelectors.blockPropertiesPanel).should('exist').and('be.visible')
406
+ })
407
+
408
+ it('SEL_BE_006_03: should find panel header', { tags: '@SEL_BE_006_03' }, () => {
409
+ cy.get(pom.editorSelectors.blockPropertiesHeader).should('exist').and('be.visible')
410
+ })
411
+
412
+ it('SEL_BE_006_04: should find block name in header', { tags: '@SEL_BE_006_04' }, () => {
413
+ cy.get(pom.editorSelectors.blockPropertiesName).should('exist').and('be.visible')
414
+ })
415
+
416
+ it('SEL_BE_006_05: should find content tab', { tags: '@SEL_BE_006_05' }, () => {
417
+ cy.get(pom.editorSelectors.tabContent).should('exist').and('be.visible')
418
+ })
419
+
420
+ it('SEL_BE_006_06: should find design tab', { tags: '@SEL_BE_006_06' }, () => {
421
+ cy.get(pom.editorSelectors.tabDesign).should('exist').and('be.visible')
422
+ })
423
+
424
+ it('SEL_BE_006_07: should find advanced tab', { tags: '@SEL_BE_006_07' }, () => {
425
+ cy.get(pom.editorSelectors.tabAdvanced).should('exist').and('be.visible')
426
+ })
427
+
428
+ it('SEL_BE_006_08: should find dynamic form container', { tags: '@SEL_BE_006_08' }, () => {
429
+ cy.get(pom.editorSelectors.dynamicForm).should('exist').and('be.visible')
430
+ })
431
+
432
+ it('SEL_BE_006_09: should find dynamic field by name', { tags: '@SEL_BE_006_09' }, () => {
433
+ // Hero block should have a title field
434
+ cy.get(pom.editorSelectors.dynamicField('title')).should('exist')
435
+ })
436
+ })
437
+ })
438
+
439
+ // ===========================================================================
440
+ // SEL_BE_007: ARRAY FIELDS SELECTORS
441
+ // ===========================================================================
442
+ describe('SEL_BE_007: Array Fields Selectors', { tags: '@SEL_BE_007' }, () => {
443
+ beforeEach(() => {
444
+ pom.visitCreate()
445
+ pom.waitForEditor()
446
+ // v2.0: Add features-grid block which has array fields
447
+ pom.addBlock('features-grid')
448
+ // Wait for block in preview canvas (v2.0 - preview mode is default)
449
+ cy.get(pom.editorSelectors.previewBlockGeneric).should('have.length.at.least', 1)
450
+ })
451
+
452
+ it('SEL_BE_007_01: should find array field container', { tags: '@SEL_BE_007_01' }, () => {
453
+ // features-grid has 'features' array field - selector is block-array-{name}
454
+ cy.get(pom.editorSelectors.arrayFieldGeneric).should('exist')
455
+ })
456
+
457
+ it('SEL_BE_007_02: should find array field add button', { tags: '@SEL_BE_007_02' }, () => {
458
+ // Selector is block-array-{name}-add
459
+ cy.get(pom.editorSelectors.arrayFieldAddGeneric).should('exist')
460
+ })
461
+
462
+ describe('With array items', () => {
463
+ beforeEach(() => {
464
+ // Click add button to add an item
465
+ cy.get(pom.editorSelectors.arrayFieldAddGeneric).first().click()
466
+ // Wait for item controls to appear - use remove button as indicator
467
+ // Selector pattern is block-array-{name}-{index}-remove
468
+ cy.get(pom.editorSelectors.arrayFieldRemoveGeneric, { timeout: 10000 }).should('have.length.at.least', 1)
469
+ })
470
+
471
+ it('SEL_BE_007_03: should find array field item controls', { tags: '@SEL_BE_007_03' }, () => {
472
+ // Item exists if it has remove button
473
+ cy.get(pom.editorSelectors.arrayFieldRemoveGeneric).first().should('exist')
474
+ })
475
+
476
+ it('SEL_BE_007_04: should find array field item move up button', { tags: '@SEL_BE_007_04' }, () => {
477
+ // Add second item for move buttons to be enabled
478
+ cy.get(pom.editorSelectors.arrayFieldAddGeneric).first().click()
479
+ cy.get(pom.editorSelectors.arrayFieldRemoveGeneric).should('have.length.at.least', 2)
480
+ // Selector is block-array-{name}-{index}-up
481
+ cy.get(pom.editorSelectors.arrayFieldUpGeneric).should('exist')
482
+ })
483
+
484
+ it('SEL_BE_007_05: should find array field item move down button', { tags: '@SEL_BE_007_05' }, () => {
485
+ // Selector is block-array-{name}-{index}-down
486
+ cy.get(pom.editorSelectors.arrayFieldDownGeneric).should('exist')
487
+ })
488
+
489
+ it('SEL_BE_007_06: should find array field item remove button', { tags: '@SEL_BE_007_06' }, () => {
490
+ // Selector is block-array-{name}-{index}-remove
491
+ cy.get(pom.editorSelectors.arrayFieldRemoveGeneric).should('exist')
492
+ })
493
+ })
494
+ })
495
+
496
+ // ===========================================================================
497
+ // SEL_BE_008: PATTERNS TAB SELECTORS
498
+ // ===========================================================================
499
+ describe('SEL_BE_008: Patterns Tab Selectors', { tags: ['@SEL_BE_008', '@patterns'] }, () => {
500
+ beforeEach(() => {
501
+ pom.visitCreate()
502
+ pom.waitForEditor()
503
+ })
504
+
505
+ it('SEL_BE_008_01: should find patterns tab button', { tags: '@SEL_BE_008_01' }, () => {
506
+ cy.get(pom.editorSelectors.tabPatterns).should('exist').and('be.visible')
507
+ })
508
+
509
+ it('SEL_BE_008_02: should find patterns search input after clicking patterns tab', { tags: '@SEL_BE_008_02' }, () => {
510
+ cy.get(pom.editorSelectors.tabPatterns).click()
511
+ cy.get(pom.editorSelectors.patternsSearch).should('exist').and('be.visible')
512
+ })
513
+
514
+ it('SEL_BE_008_03: should find patterns list container', { tags: '@SEL_BE_008_03' }, () => {
515
+ cy.get(pom.editorSelectors.tabPatterns).click()
516
+ cy.get(pom.editorSelectors.patternsList).should('exist').and('be.visible')
517
+ })
518
+
519
+ it('SEL_BE_008_04: should find pattern cards for sample patterns', { tags: '@SEL_BE_008_04' }, () => {
520
+ cy.get(pom.editorSelectors.tabPatterns).click()
521
+ // Wait for patterns to load
522
+ cy.wait(500)
523
+ // Check for pattern cards with specific IDs (using generic selector)
524
+ // Pattern cards should exist for published patterns
525
+ cy.get(pom.editorSelectors.patternCardGeneric).should('have.length.at.least', 1)
526
+ })
527
+
528
+ it('SEL_BE_008_05: should find pattern card icon element', { tags: '@SEL_BE_008_05' }, () => {
529
+ cy.get(pom.editorSelectors.tabPatterns).click()
530
+ cy.wait(500)
531
+ // Get first pattern ID and use dynamic selector
532
+ cy.get(pom.editorSelectors.patternCardGeneric).first()
533
+ .invoke('attr', 'data-cy')
534
+ .then((dataCy) => {
535
+ const id = dataCy?.replace('block-picker-pattern-card-', '') || ''
536
+ cy.get(pom.editorSelectors.patternCardIcon(id)).should('exist')
537
+ })
538
+ })
539
+
540
+ it('SEL_BE_008_06: should find pattern card title element', { tags: '@SEL_BE_008_06' }, () => {
541
+ cy.get(pom.editorSelectors.tabPatterns).click()
542
+ cy.wait(500)
543
+ // Get first pattern ID and use dynamic selector
544
+ cy.get(pom.editorSelectors.patternCardGeneric).first()
545
+ .invoke('attr', 'data-cy')
546
+ .then((dataCy) => {
547
+ const id = dataCy?.replace('block-picker-pattern-card-', '') || ''
548
+ cy.get(pom.editorSelectors.patternCardTitle(id)).should('exist')
549
+ })
550
+ })
551
+
552
+ it('SEL_BE_008_07: should find pattern card description element', { tags: '@SEL_BE_008_07' }, () => {
553
+ cy.get(pom.editorSelectors.tabPatterns).click()
554
+ cy.wait(500)
555
+ // Get first pattern ID and use dynamic selector
556
+ cy.get(pom.editorSelectors.patternCardGeneric).first()
557
+ .invoke('attr', 'data-cy')
558
+ .then((dataCy) => {
559
+ const id = dataCy?.replace('block-picker-pattern-card-', '') || ''
560
+ cy.get(pom.editorSelectors.patternCardDescription(id)).should('exist')
561
+ })
562
+ })
563
+
564
+ it('SEL_BE_008_08: should find pattern card insert button', { tags: '@SEL_BE_008_08' }, () => {
565
+ cy.get(pom.editorSelectors.tabPatterns).click()
566
+ cy.wait(500)
567
+ // Get first pattern ID and use dynamic selector
568
+ cy.get(pom.editorSelectors.patternCardGeneric).first()
569
+ .invoke('attr', 'data-cy')
570
+ .then((dataCy) => {
571
+ const id = dataCy?.replace('block-picker-pattern-card-', '') || ''
572
+ cy.get(pom.editorSelectors.patternCardInsertButton(id)).should('exist')
573
+ })
574
+ })
575
+ })
576
+
577
+ // ===========================================================================
578
+ // SEL_BE_009: PATTERN REFERENCE SELECTORS
579
+ // ===========================================================================
580
+ describe('SEL_BE_009: Pattern Reference Selectors', { tags: ['@SEL_BE_009', '@patterns'] }, () => {
581
+ let patternRefId: string
582
+
583
+ beforeEach(() => {
584
+ pom.visitCreate()
585
+ pom.waitForEditor()
586
+ pom.switchToLayoutMode()
587
+
588
+ // Insert a pattern to create a pattern reference
589
+ cy.get(pom.editorSelectors.tabPatterns).click()
590
+ cy.wait(500)
591
+
592
+ // Click insert on first available pattern
593
+ cy.get(pom.editorSelectors.patternCardGeneric).first()
594
+ .invoke('attr', 'data-cy')
595
+ .then((dataCy) => {
596
+ const id = dataCy?.replace('block-picker-pattern-card-', '') || ''
597
+ cy.get(pom.editorSelectors.patternCardInsertButton(id)).click()
598
+ })
599
+
600
+ // Wait for pattern reference to be created
601
+ cy.wait(1000)
602
+
603
+ // Switch to preview mode to see pattern reference rendering
604
+ pom.switchToPreviewMode()
605
+
606
+ // Get the pattern reference ID from the first pattern-reference element
607
+ cy.get(pom.editorSelectors.patternReferenceGeneric).first()
608
+ .invoke('attr', 'data-cy')
609
+ .then((dataCy) => {
610
+ patternRefId = dataCy?.replace('pattern-reference-', '') || ''
611
+ cy.log(`Pattern reference ID: ${patternRefId}`)
612
+ })
613
+ })
614
+
615
+ it('SEL_BE_009_01: should find pattern reference container', { tags: '@SEL_BE_009_01' }, () => {
616
+ cy.get(pom.editorSelectors.patternReferenceGeneric).should('exist').and('be.visible')
617
+ })
618
+
619
+ it('SEL_BE_009_02: should find pattern reference badge', { tags: '@SEL_BE_009_02' }, () => {
620
+ // Get ref from first pattern-reference, then use dynamic selector
621
+ cy.get(pom.editorSelectors.patternReferenceGeneric).first()
622
+ .invoke('attr', 'data-cy')
623
+ .then((dataCy) => {
624
+ const ref = dataCy?.replace('pattern-reference-', '') || ''
625
+ cy.get(pom.editorSelectors.patternReferenceBadge(ref)).should('exist')
626
+ })
627
+ })
628
+
629
+ it('SEL_BE_009_03: should find pattern reference remove button', { tags: '@SEL_BE_009_03' }, () => {
630
+ // Get ref from first pattern-reference, then use dynamic selector
631
+ cy.get(pom.editorSelectors.patternReferenceGeneric).first()
632
+ .invoke('attr', 'data-cy')
633
+ .then((dataCy) => {
634
+ const ref = dataCy?.replace('pattern-reference-', '') || ''
635
+ cy.get(pom.editorSelectors.patternReferenceRemove(ref)).should('exist')
636
+ })
637
+ })
638
+
639
+ it.skip('SEL_BE_009_04: should find pattern reference locked state when selected', { tags: '@SEL_BE_009_04' }, () => {
640
+ // SKIPPED: patternReferenceLocked selector is defined in core but not yet implemented in component
641
+ // TODO: Implement locked state visual indicator for pattern references
642
+ cy.get(pom.editorSelectors.patternReferenceGeneric).first().click()
643
+ cy.wait(500)
644
+ cy.get(pom.editorSelectors.patternReferenceGeneric).first()
645
+ .invoke('attr', 'data-cy')
646
+ .then((dataCy) => {
647
+ const ref = dataCy?.replace('pattern-reference-', '') || ''
648
+ cy.get(pom.editorSelectors.patternReferenceLocked(ref)).should('exist')
649
+ })
650
+ })
651
+
652
+ it('SEL_BE_009_05: should find pattern reference edit link', { tags: '@SEL_BE_009_05' }, () => {
653
+ // Get ref from first pattern-reference, then use dynamic selector
654
+ cy.get(pom.editorSelectors.patternReferenceGeneric).first()
655
+ .invoke('attr', 'data-cy')
656
+ .then((dataCy) => {
657
+ const ref = dataCy?.replace('pattern-reference-', '') || ''
658
+ cy.get(pom.editorSelectors.patternReferenceEditLink(ref)).should('exist')
659
+ })
660
+ })
661
+ })
662
+
663
+ // ===========================================================================
664
+ // SEL_BE_010: CONFIG PANEL SEO & CUSTOM FIELDS SELECTORS (v2.1)
665
+ // ===========================================================================
666
+ describe('SEL_BE_010: Config Panel SEO & Custom Fields Selectors', { tags: '@SEL_BE_010' }, () => {
667
+ let testPageId: string
668
+
669
+ before(() => {
670
+ loginAsDefaultDeveloper()
671
+ cy.request({
672
+ method: 'POST',
673
+ url: '/api/v1/pages',
674
+ headers: {
675
+ 'x-team-id': DEVELOPER_TEAM_ID,
676
+ 'Content-Type': 'application/json'
677
+ },
678
+ body: {
679
+ title: `SEO Test Page ${Date.now()}`,
680
+ slug: `seo-test-${Date.now()}`,
681
+ locale: 'en',
682
+ published: false,
683
+ blocks: []
684
+ }
685
+ }).then((response) => {
686
+ testPageId = response.body.data.id
687
+ })
688
+ })
689
+
690
+ beforeEach(() => {
691
+ pom.visitEdit(testPageId)
692
+ pom.waitForEditor()
693
+ cy.get(pom.editorSelectors.viewSettings).click()
694
+ })
695
+
696
+ after(() => {
697
+ if (testPageId) {
698
+ cy.request({
699
+ method: 'DELETE',
700
+ url: `/api/v1/pages/${testPageId}`,
701
+ headers: { 'x-team-id': DEVELOPER_TEAM_ID },
702
+ failOnStatusCode: false
703
+ })
704
+ }
705
+ })
706
+
707
+ // SEO Section tests
708
+ describe('SEO Section', () => {
709
+ it('SEL_BE_010_01: should find SEO section container', { tags: '@SEL_BE_010_01' }, () => {
710
+ cy.get(pom.editorSelectors.configSeoSectionContainer).should('exist').and('be.visible')
711
+ })
712
+
713
+ it('SEL_BE_010_02: should find SEO section trigger', { tags: '@SEL_BE_010_02' }, () => {
714
+ cy.get(pom.editorSelectors.configSeoSectionTrigger).should('exist').and('be.visible')
715
+ })
716
+
717
+ it('SEL_BE_010_03: should find SEO section content after click', { tags: '@SEL_BE_010_03' }, () => {
718
+ cy.get(pom.editorSelectors.configSeoSectionTrigger).click()
719
+ cy.get(pom.editorSelectors.configSeoSectionContent).should('exist').and('be.visible')
720
+ })
721
+
722
+ it('SEL_BE_010_04: should find meta title input', { tags: '@SEL_BE_010_04' }, () => {
723
+ cy.get(pom.editorSelectors.configSeoSectionTrigger).click()
724
+ cy.get(pom.editorSelectors.configSeoMetaTitle).should('exist').and('be.visible')
725
+ })
726
+
727
+ it('SEL_BE_010_05: should find meta description input', { tags: '@SEL_BE_010_05' }, () => {
728
+ cy.get(pom.editorSelectors.configSeoSectionTrigger).click()
729
+ cy.get(pom.editorSelectors.configSeoMetaDescription).should('exist').and('be.visible')
730
+ })
731
+
732
+ it('SEL_BE_010_06: should find meta keywords input', { tags: '@SEL_BE_010_06' }, () => {
733
+ cy.get(pom.editorSelectors.configSeoSectionTrigger).click()
734
+ cy.get(pom.editorSelectors.configSeoMetaKeywords).should('exist').and('be.visible')
735
+ })
736
+
737
+ it('SEL_BE_010_07: should find OG image input', { tags: '@SEL_BE_010_07' }, () => {
738
+ cy.get(pom.editorSelectors.configSeoSectionTrigger).click()
739
+ cy.get(pom.editorSelectors.configSeoOgImage).should('exist').and('be.visible')
740
+ })
741
+ })
742
+
743
+ // Custom Fields Section tests
744
+ describe('Custom Fields Section', () => {
745
+ it('SEL_BE_010_08: should find custom fields section container', { tags: '@SEL_BE_010_08' }, () => {
746
+ cy.get(pom.editorSelectors.configCustomFieldsContainer).should('exist').and('be.visible')
747
+ })
748
+
749
+ it('SEL_BE_010_09: should find custom fields section trigger', { tags: '@SEL_BE_010_09' }, () => {
750
+ cy.get(pom.editorSelectors.configCustomFieldsTrigger).should('exist').and('be.visible')
751
+ })
752
+
753
+ it('SEL_BE_010_10: should find custom fields content after click', { tags: '@SEL_BE_010_10' }, () => {
754
+ cy.get(pom.editorSelectors.configCustomFieldsTrigger).click()
755
+ cy.get(pom.editorSelectors.configCustomFieldsContent).should('exist').and('be.visible')
756
+ })
757
+
758
+ it('SEL_BE_010_11: should find add custom field button', { tags: '@SEL_BE_010_11' }, () => {
759
+ cy.get(pom.editorSelectors.configCustomFieldsTrigger).click()
760
+ cy.get(pom.editorSelectors.configCustomFieldsAddBtn).should('exist').and('be.visible')
761
+ })
762
+
763
+ describe('With custom field added', () => {
764
+ beforeEach(() => {
765
+ cy.get(pom.editorSelectors.configCustomFieldsTrigger).click()
766
+ cy.get(pom.editorSelectors.configCustomFieldsAddBtn).click()
767
+ })
768
+
769
+ it('SEL_BE_010_12: should find custom field key input', { tags: '@SEL_BE_010_12' }, () => {
770
+ cy.get(pom.editorSelectors.configCustomFieldKey(0)).should('exist').and('be.visible')
771
+ })
772
+
773
+ it('SEL_BE_010_13: should find custom field value input', { tags: '@SEL_BE_010_13' }, () => {
774
+ cy.get(pom.editorSelectors.configCustomFieldValue(0)).should('exist').and('be.visible')
775
+ })
776
+
777
+ it('SEL_BE_010_14: should find custom field remove button', { tags: '@SEL_BE_010_14' }, () => {
778
+ cy.get(pom.editorSelectors.configCustomFieldRemove(0)).should('exist').and('be.visible')
779
+ })
780
+ })
781
+ })
782
+ })
783
+ })