@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,559 @@
1
+ /**
2
+ * UI Selectors Validation: Patterns
3
+ *
4
+ * This test validates that patterns-specific selectors exist in the DOM.
5
+ * This is a lightweight test that ONLY checks selector presence, not functionality.
6
+ *
7
+ * Purpose:
8
+ * - Validate PatternsPOM and PageBuilderPOM selectors work correctly
9
+ * - Ensure all patterns-specific selectors are implemented
10
+ * - Validate KEY DIFFERENCES from standard entity editor:
11
+ * - NO slug input (patterns don't have slugs)
12
+ * - NO patterns tab in block picker (to prevent recursive patterns)
13
+ * - Run as Phase 12 sub-gate before functional tests
14
+ *
15
+ * Scope:
16
+ * - Navigate to patterns pages (requires login)
17
+ * - Assert elements exist in DOM (no form submissions)
18
+ * - Fast execution (< 30 seconds per describe block)
19
+ *
20
+ * Test IDs:
21
+ * - SEL_PAT_001: Patterns List Page Selectors (5 selectors)
22
+ * - SEL_PAT_002: Patterns List Table Actions (4 selectors)
23
+ * - SEL_PAT_003: Patterns Editor Header - KEY DIFFERENCES (5 selectors)
24
+ * - SEL_PAT_004: Patterns Editor Block Picker - KEY DIFFERENCES (5 selectors)
25
+ * - SEL_PAT_005: Patterns Reports Page (4 selectors)
26
+ * - SEL_PAT_006: Pattern Usage Stats (3 selectors)
27
+ * - SEL_PAT_007: Pattern Usage Report Controls (5 selectors)
28
+ * - SEL_PAT_008: Pattern Usage Table (3 selectors)
29
+ *
30
+ * DEPENDENCIES:
31
+ * - Sample patterns must exist (from seed data)
32
+ * - At least one pattern must have usages (for reports tests)
33
+ */
34
+
35
+ import { PatternsPOM } from '../../../src/entities/PatternsPOM'
36
+ import { cySelector } from '../../../src/selectors'
37
+ import { loginAsDefaultDeveloper } from '../../../src/session-helpers'
38
+
39
+ describe('Patterns Selectors Validation', { tags: ['@ui-selectors', '@patterns', '@feat-patterns'] }, () => {
40
+ const patterns = PatternsPOM.create()
41
+
42
+ // ============================================
43
+ // SEL_PAT_001: PATTERNS LIST PAGE SELECTORS (5 selectors)
44
+ // ============================================
45
+ describe('SEL_PAT_001: Patterns List Page Selectors', { tags: '@SEL_PAT_001' }, () => {
46
+ beforeEach(() => {
47
+ loginAsDefaultDeveloper()
48
+ patterns.visitList()
49
+ patterns.waitForList()
50
+ })
51
+
52
+ it('SEL_PAT_001_01: should find page container', { tags: '@SEL_PAT_001_01' }, () => {
53
+ cy.get(patterns.selectors.page).should('exist')
54
+ })
55
+
56
+ it('SEL_PAT_001_02: should find page title', { tags: '@SEL_PAT_001_02' }, () => {
57
+ cy.get(patterns.selectors.title).should('exist')
58
+ })
59
+
60
+ it('SEL_PAT_001_03: should find add button', { tags: '@SEL_PAT_001_03' }, () => {
61
+ cy.get(patterns.selectors.addButton).should('exist')
62
+ })
63
+
64
+ it('SEL_PAT_001_04: should find search container', { tags: '@SEL_PAT_001_04' }, () => {
65
+ cy.get(patterns.selectors.searchContainer).should('exist')
66
+ })
67
+
68
+ it('SEL_PAT_001_05: should find table container (or empty state)', { tags: '@SEL_PAT_001_05' }, () => {
69
+ // Check for table OR empty state - both are valid depending on data
70
+ cy.get('body').then(($body) => {
71
+ if ($body.find(patterns.selectors.table).length > 0) {
72
+ cy.get(patterns.selectors.table).should('exist')
73
+ } else if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
74
+ // No patterns exist, empty state is acceptable
75
+ cy.log('⚠️ Table not found - patterns may not exist (empty state is valid)')
76
+ } else {
77
+ // Patterns exist but table not found - this is a real error
78
+ cy.get(patterns.selectors.table).should('exist')
79
+ }
80
+ })
81
+ })
82
+ })
83
+
84
+ // ============================================
85
+ // SEL_PAT_002: PATTERNS LIST TABLE ACTIONS (4 selectors)
86
+ // REQUIRES: At least one pattern in the database
87
+ // NOTE: These tests are skipped if no patterns exist (data-dependent)
88
+ // ============================================
89
+ describe('SEL_PAT_002: Patterns List Table Actions', { tags: '@SEL_PAT_002' }, () => {
90
+ beforeEach(() => {
91
+ loginAsDefaultDeveloper()
92
+ patterns.visitList()
93
+ patterns.waitForList()
94
+ })
95
+
96
+ it('SEL_PAT_002_01: should find row menu for first pattern', { tags: '@SEL_PAT_002_01' }, () => {
97
+ // Check if patterns exist, skip if none
98
+ cy.get('body').then(($body) => {
99
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
100
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
101
+ return // Skip the rest of the test
102
+ }
103
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
104
+ const dataCy = $row.attr('data-cy')
105
+ const patternId = dataCy?.replace('patterns-row-', '')
106
+ if (patternId) {
107
+ cy.get(patterns.selectors.rowMenu(patternId)).should('exist')
108
+ }
109
+ })
110
+ })
111
+ })
112
+
113
+ it('SEL_PAT_002_02: should find edit action in row menu', { tags: '@SEL_PAT_002_02' }, () => {
114
+ cy.get('body').then(($body) => {
115
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
116
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
117
+ return
118
+ }
119
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
120
+ const dataCy = $row.attr('data-cy')
121
+ const patternId = dataCy?.replace('patterns-row-', '')
122
+ if (patternId) {
123
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
124
+ cy.get(patterns.selectors.rowAction('edit', patternId)).should('exist')
125
+ }
126
+ })
127
+ })
128
+ })
129
+
130
+ it('SEL_PAT_002_03: should find delete action in row menu', { tags: '@SEL_PAT_002_03' }, () => {
131
+ cy.get('body').then(($body) => {
132
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
133
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
134
+ return
135
+ }
136
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
137
+ const dataCy = $row.attr('data-cy')
138
+ const patternId = dataCy?.replace('patterns-row-', '')
139
+ if (patternId) {
140
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
141
+ cy.get(patterns.selectors.rowAction('delete', patternId)).should('exist')
142
+ }
143
+ })
144
+ })
145
+ })
146
+
147
+ it('SEL_PAT_002_04: should find usages quick action in row menu', { tags: '@SEL_PAT_002_04' }, () => {
148
+ cy.get('body').then(($body) => {
149
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
150
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
151
+ return
152
+ }
153
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
154
+ const dataCy = $row.attr('data-cy')
155
+ const patternId = dataCy?.replace('patterns-row-', '')
156
+ if (patternId) {
157
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
158
+ cy.get(patterns.selectors.rowAction('usages', patternId)).should('exist')
159
+ }
160
+ })
161
+ })
162
+ })
163
+ })
164
+
165
+ // ============================================
166
+ // SEL_PAT_003: PATTERNS EDITOR HEADER - KEY DIFFERENCES (5 selectors)
167
+ // NOTE: Patterns editor does NOT have slug input (unlike pages/posts)
168
+ // ============================================
169
+ describe('SEL_PAT_003: Patterns Editor Header', { tags: '@SEL_PAT_003' }, () => {
170
+ beforeEach(() => {
171
+ loginAsDefaultDeveloper()
172
+ // Navigate to create a new pattern
173
+ cy.visit('/dashboard/patterns/create', { timeout: 60000 })
174
+ // Wait for editor to load using block editor selector
175
+ cy.get(cySelector('blockEditor.header.container'), { timeout: 15000 }).should('be.visible')
176
+ })
177
+
178
+ it('SEL_PAT_003_01: should find header container', { tags: '@SEL_PAT_003_01' }, () => {
179
+ cy.get(cySelector('blockEditor.header.container')).should('exist')
180
+ })
181
+
182
+ it('SEL_PAT_003_02: should find title input', { tags: '@SEL_PAT_003_02' }, () => {
183
+ cy.get(cySelector('blockEditor.header.titleInput')).should('exist')
184
+ })
185
+
186
+ it('SEL_PAT_003_03: should NOT have slug input (patterns-specific)', { tags: '@SEL_PAT_003_03' }, () => {
187
+ // KEY DIFFERENCE: Patterns do not have slugs
188
+ cy.get(cySelector('blockEditor.header.slugInput')).should('not.exist')
189
+ })
190
+
191
+ it('SEL_PAT_003_04: should find save button', { tags: '@SEL_PAT_003_04' }, () => {
192
+ cy.get(cySelector('blockEditor.header.saveButton')).should('exist')
193
+ })
194
+
195
+ it('SEL_PAT_003_05: should find view mode toggle', { tags: '@SEL_PAT_003_05' }, () => {
196
+ cy.get(cySelector('blockEditor.header.viewToggle')).should('exist')
197
+ })
198
+ })
199
+
200
+ // ============================================
201
+ // SEL_PAT_004: PATTERNS EDITOR BLOCK PICKER - KEY DIFFERENCES (5 selectors)
202
+ // NOTE: Patterns block picker does NOT have patterns tab (to prevent recursion)
203
+ // ============================================
204
+ describe('SEL_PAT_004: Patterns Editor Block Picker', { tags: '@SEL_PAT_004' }, () => {
205
+ beforeEach(() => {
206
+ loginAsDefaultDeveloper()
207
+ cy.visit('/dashboard/patterns/create', { timeout: 60000 })
208
+ cy.get(cySelector('blockEditor.header.container'), { timeout: 15000 }).should('be.visible')
209
+ })
210
+
211
+ it('SEL_PAT_004_01: should find block picker container', { tags: '@SEL_PAT_004_01' }, () => {
212
+ cy.get(cySelector('blockEditor.blockPicker.container')).should('exist')
213
+ })
214
+
215
+ it('SEL_PAT_004_02: should find blocks tab', { tags: '@SEL_PAT_004_02' }, () => {
216
+ cy.get(cySelector('blockEditor.blockPicker.tabBlocks')).should('exist')
217
+ })
218
+
219
+ it('SEL_PAT_004_03: should NOT have patterns tab (patterns-specific)', { tags: '@SEL_PAT_004_03' }, () => {
220
+ // KEY DIFFERENCE: Patterns cannot contain other patterns (prevent recursion)
221
+ cy.get(cySelector('blockEditor.blockPicker.tabPatterns')).should('not.exist')
222
+ })
223
+
224
+ it.skip('SEL_PAT_004_04: should find config tab (skipped - patterns have no sidebarFields/taxonomies)', { tags: '@SEL_PAT_004_04' }, () => {
225
+ // Config tab only appears when entity has sidebarFields or taxonomies enabled
226
+ // Patterns entity doesn't have either, so this tab is correctly hidden
227
+ cy.get(cySelector('blockEditor.blockPicker.tabConfig')).should('exist')
228
+ })
229
+
230
+ it('SEL_PAT_004_05: should find search input', { tags: '@SEL_PAT_004_05' }, () => {
231
+ cy.get(cySelector('blockEditor.blockPicker.searchInput')).should('exist')
232
+ })
233
+ })
234
+
235
+ // ============================================
236
+ // SEL_PAT_005: PATTERNS REPORTS PAGE (4 selectors)
237
+ // REQUIRES: A pattern with usages for meaningful tests
238
+ // NOTE: These tests are skipped if no patterns exist (data-dependent)
239
+ // ============================================
240
+ describe('SEL_PAT_005: Patterns Reports Page', { tags: '@SEL_PAT_005' }, () => {
241
+ beforeEach(() => {
242
+ loginAsDefaultDeveloper()
243
+ patterns.visitList()
244
+ patterns.waitForList()
245
+ })
246
+
247
+ it('SEL_PAT_005_01: should navigate to pattern usages and find report container', { tags: '@SEL_PAT_005_01' }, () => {
248
+ cy.get('body').then(($body) => {
249
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
250
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
251
+ return
252
+ }
253
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
254
+ const dataCy = $row.attr('data-cy')
255
+ const patternId = dataCy?.replace('patterns-row-', '')
256
+ if (patternId) {
257
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
258
+ cy.get(patterns.selectors.rowAction('usages', patternId)).click()
259
+ cy.get(patterns.patternSelectors.usageReport.container, { timeout: 15000 }).should('exist')
260
+ }
261
+ })
262
+ })
263
+ })
264
+
265
+ it('SEL_PAT_005_02: should find back button on usages page', { tags: '@SEL_PAT_005_02' }, () => {
266
+ cy.get('body').then(($body) => {
267
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
268
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
269
+ return
270
+ }
271
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
272
+ const dataCy = $row.attr('data-cy')
273
+ const patternId = dataCy?.replace('patterns-row-', '')
274
+ if (patternId) {
275
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
276
+ cy.get(patterns.selectors.rowAction('usages', patternId)).click()
277
+ cy.get(patterns.patternSelectors.usageReport.container, { timeout: 15000 }).should('exist')
278
+ cy.get(patterns.selectors.backButton).should('exist')
279
+ }
280
+ })
281
+ })
282
+ })
283
+
284
+ it('SEL_PAT_005_03: should find page title on usages page', { tags: '@SEL_PAT_005_03' }, () => {
285
+ cy.get('body').then(($body) => {
286
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
287
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
288
+ return
289
+ }
290
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
291
+ const dataCy = $row.attr('data-cy')
292
+ const patternId = dataCy?.replace('patterns-row-', '')
293
+ if (patternId) {
294
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
295
+ cy.get(patterns.selectors.rowAction('usages', patternId)).click()
296
+ cy.get(patterns.patternSelectors.usageReport.container, { timeout: 15000 }).should('exist')
297
+ cy.get(patterns.selectors.headerTitle).should('exist')
298
+ }
299
+ })
300
+ })
301
+ })
302
+
303
+ it('SEL_PAT_005_04: should find edit button on usages page', { tags: '@SEL_PAT_005_04' }, () => {
304
+ cy.get('body').then(($body) => {
305
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
306
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
307
+ return
308
+ }
309
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
310
+ const dataCy = $row.attr('data-cy')
311
+ const patternId = dataCy?.replace('patterns-row-', '')
312
+ if (patternId) {
313
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
314
+ cy.get(patterns.selectors.rowAction('usages', patternId)).click()
315
+ cy.get(patterns.patternSelectors.usageReport.container, { timeout: 15000 }).should('exist')
316
+ cy.get(patterns.selectors.editButton).should('exist')
317
+ }
318
+ })
319
+ })
320
+ })
321
+ })
322
+
323
+ // ============================================
324
+ // SEL_PAT_006: PATTERN USAGE STATS (3 selectors)
325
+ // NOTE: These tests are skipped if no patterns exist (data-dependent)
326
+ // ============================================
327
+ describe('SEL_PAT_006: Pattern Usage Stats', { tags: '@SEL_PAT_006' }, () => {
328
+ // Helper to navigate to usages page if patterns exist
329
+ const navigateToUsagesIfPatternsExist = () => {
330
+ return cy.get('body').then(($body) => {
331
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
332
+ return false
333
+ }
334
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
335
+ const dataCy = $row.attr('data-cy')
336
+ const patternId = dataCy?.replace('patterns-row-', '')
337
+ if (patternId) {
338
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
339
+ cy.get(patterns.selectors.rowAction('usages', patternId)).click()
340
+ cy.get(patterns.patternSelectors.usageReport.container, { timeout: 15000 }).should('exist')
341
+ }
342
+ })
343
+ return true
344
+ })
345
+ }
346
+
347
+ beforeEach(() => {
348
+ loginAsDefaultDeveloper()
349
+ patterns.visitList()
350
+ patterns.waitForList()
351
+ })
352
+
353
+ it('SEL_PAT_006_01: should find stats container', { tags: '@SEL_PAT_006_01' }, () => {
354
+ cy.get('body').then(($body) => {
355
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
356
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
357
+ return
358
+ }
359
+ navigateToUsagesIfPatternsExist().then(() => {
360
+ cy.get(patterns.patternSelectors.usageStats.container).should('exist')
361
+ })
362
+ })
363
+ })
364
+
365
+ it('SEL_PAT_006_02: should find total usage card', { tags: '@SEL_PAT_006_02' }, () => {
366
+ cy.get('body').then(($body) => {
367
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
368
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
369
+ return
370
+ }
371
+ navigateToUsagesIfPatternsExist().then(() => {
372
+ cy.get(patterns.patternSelectors.usageStats.total).should('exist')
373
+ })
374
+ })
375
+ })
376
+
377
+ it('SEL_PAT_006_03: should find usage by type cards (if usages exist)', { tags: '@SEL_PAT_006_03' }, () => {
378
+ cy.get('body').then(($body) => {
379
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
380
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
381
+ return
382
+ }
383
+ navigateToUsagesIfPatternsExist().then(() => {
384
+ cy.get('body').then(($innerBody) => {
385
+ if ($innerBody.find(patterns.patternSelectors.usageStats.byType('pages')).length > 0) {
386
+ cy.get(patterns.patternSelectors.usageStats.byType('pages')).should('exist')
387
+ } else if ($innerBody.find(patterns.patternSelectors.usageStats.byType('posts')).length > 0) {
388
+ cy.get(patterns.patternSelectors.usageStats.byType('posts')).should('exist')
389
+ } else {
390
+ cy.log('No usage by type cards found - pattern may have no usages')
391
+ }
392
+ })
393
+ })
394
+ })
395
+ })
396
+ })
397
+
398
+ // ============================================
399
+ // SEL_PAT_007: PATTERN USAGE REPORT CONTROLS (5 selectors)
400
+ // NOTE: These tests are skipped if no patterns exist (data-dependent)
401
+ // ============================================
402
+ describe('SEL_PAT_007: Pattern Usage Report Controls', { tags: '@SEL_PAT_007' }, () => {
403
+ // Helper to navigate to usages page if patterns exist
404
+ const navigateToUsagesIfPatternsExist = () => {
405
+ return cy.get('body').then(($body) => {
406
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
407
+ return false
408
+ }
409
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
410
+ const dataCy = $row.attr('data-cy')
411
+ const patternId = dataCy?.replace('patterns-row-', '')
412
+ if (patternId) {
413
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
414
+ cy.get(patterns.selectors.rowAction('usages', patternId)).click()
415
+ cy.get(patterns.patternSelectors.usageReport.container, { timeout: 15000 }).should('exist')
416
+ }
417
+ })
418
+ return true
419
+ })
420
+ }
421
+
422
+ beforeEach(() => {
423
+ loginAsDefaultDeveloper()
424
+ patterns.visitList()
425
+ patterns.waitForList()
426
+ })
427
+
428
+ it('SEL_PAT_007_01: should find report container', { tags: '@SEL_PAT_007_01' }, () => {
429
+ cy.get('body').then(($body) => {
430
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
431
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
432
+ return
433
+ }
434
+ navigateToUsagesIfPatternsExist().then(() => {
435
+ cy.get(patterns.patternSelectors.usageReport.container).should('exist')
436
+ })
437
+ })
438
+ })
439
+
440
+ it('SEL_PAT_007_02: should find filter select', { tags: '@SEL_PAT_007_02' }, () => {
441
+ cy.get('body').then(($body) => {
442
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
443
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
444
+ return
445
+ }
446
+ navigateToUsagesIfPatternsExist().then(() => {
447
+ cy.get(patterns.patternSelectors.usageReport.filterSelect).should('exist')
448
+ })
449
+ })
450
+ })
451
+
452
+ it('SEL_PAT_007_03: should find pagination container', { tags: '@SEL_PAT_007_03' }, () => {
453
+ cy.get('body').then(($body) => {
454
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
455
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
456
+ return
457
+ }
458
+ navigateToUsagesIfPatternsExist().then(() => {
459
+ cy.get(patterns.patternSelectors.usageReport.pagination).should('exist')
460
+ })
461
+ })
462
+ })
463
+
464
+ it('SEL_PAT_007_04: should find prev page button', { tags: '@SEL_PAT_007_04' }, () => {
465
+ cy.get('body').then(($body) => {
466
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
467
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
468
+ return
469
+ }
470
+ navigateToUsagesIfPatternsExist().then(() => {
471
+ cy.get(patterns.patternSelectors.usageReport.prevPage).should('exist')
472
+ })
473
+ })
474
+ })
475
+
476
+ it('SEL_PAT_007_05: should find next page button', { tags: '@SEL_PAT_007_05' }, () => {
477
+ cy.get('body').then(($body) => {
478
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
479
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
480
+ return
481
+ }
482
+ navigateToUsagesIfPatternsExist().then(() => {
483
+ cy.get(patterns.patternSelectors.usageReport.nextPage).should('exist')
484
+ })
485
+ })
486
+ })
487
+
488
+ it('SEL_PAT_007_06: should find results info', { tags: '@SEL_PAT_007_06' }, () => {
489
+ cy.get('body').then(($body) => {
490
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
491
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
492
+ return
493
+ }
494
+ navigateToUsagesIfPatternsExist().then(() => {
495
+ cy.get(patterns.patternSelectors.usageReport.resultsInfo).should('exist')
496
+ })
497
+ })
498
+ })
499
+ })
500
+
501
+ // ============================================
502
+ // SEL_PAT_008: PATTERN USAGE TABLE (3 selectors)
503
+ // NOTE: These tests are skipped if no patterns exist (data-dependent)
504
+ // ============================================
505
+ describe('SEL_PAT_008: Pattern Usage Table', { tags: '@SEL_PAT_008' }, () => {
506
+ // Helper to navigate to usages page if patterns exist
507
+ const navigateToUsagesIfPatternsExist = () => {
508
+ return cy.get('body').then(($body) => {
509
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
510
+ return false
511
+ }
512
+ cy.get('[data-cy^="patterns-row-"]').first().then(($row) => {
513
+ const dataCy = $row.attr('data-cy')
514
+ const patternId = dataCy?.replace('patterns-row-', '')
515
+ if (patternId) {
516
+ cy.get(patterns.selectors.rowMenu(patternId)).click()
517
+ cy.get(patterns.selectors.rowAction('usages', patternId)).click()
518
+ cy.get(patterns.patternSelectors.usageReport.container, { timeout: 15000 }).should('exist')
519
+ }
520
+ })
521
+ return true
522
+ })
523
+ }
524
+
525
+ beforeEach(() => {
526
+ loginAsDefaultDeveloper()
527
+ patterns.visitList()
528
+ patterns.waitForList()
529
+ })
530
+
531
+ it('SEL_PAT_008_01: should find usage table container (or empty state)', { tags: '@SEL_PAT_008_01' }, () => {
532
+ cy.get('body').then(($body) => {
533
+ if ($body.find('[data-cy^="patterns-row-"]').length === 0) {
534
+ cy.log('⚠️ No patterns found - skipping test (data-dependent)')
535
+ return
536
+ }
537
+ navigateToUsagesIfPatternsExist().then(() => {
538
+ cy.get('body').then(($innerBody) => {
539
+ if ($innerBody.find(patterns.patternSelectors.usageTable.container).length > 0) {
540
+ cy.get(patterns.patternSelectors.usageTable.container).should('exist')
541
+ } else {
542
+ cy.get(patterns.patternSelectors.usageTable.empty).should('exist')
543
+ }
544
+ })
545
+ })
546
+ })
547
+ })
548
+
549
+ it.skip('SEL_PAT_008_02: should find usage row (requires pattern with usages)', { tags: '@SEL_PAT_008_02' }, () => {
550
+ // This test requires the pattern to have actual usages
551
+ cy.get('[data-cy^="pattern-usage-row-"]').first().should('exist')
552
+ })
553
+
554
+ it.skip('SEL_PAT_008_03: should find view link in usage row (requires pattern with usages)', { tags: '@SEL_PAT_008_03' }, () => {
555
+ // This test requires the pattern to have actual usages
556
+ cy.get('[data-cy^="pattern-usage-view-"]').first().should('exist')
557
+ })
558
+ })
559
+ })
@@ -0,0 +1,112 @@
1
+ /**
2
+ * UI Selectors Validation: Public Pages
3
+ *
4
+ * This test validates that public page selectors exist in the DOM.
5
+ * This is a lightweight test that ONLY checks selector presence, not functionality.
6
+ *
7
+ * Purpose:
8
+ * - Validate PublicPagePOM navbar/footer selectors work correctly
9
+ * - Ensure all public.* selectors are implemented
10
+ * - Run as Phase 12 sub-gate before functional tests
11
+ *
12
+ * Scope:
13
+ * - Navigate to public pages (NO login required)
14
+ * - Assert elements exist in DOM (no form submissions)
15
+ * - Fast execution (< 30 seconds per describe block)
16
+ *
17
+ * NOTE: Public pages use PublicNavbar and PublicFooter from core.
18
+ * Blog and page selectors (public.page.*, public.blog.*) are pending
19
+ * as they require specific content to exist.
20
+ */
21
+
22
+ import { PublicPagePOM } from '../../../src/components/PublicPagePOM'
23
+
24
+ describe('Public Page Selectors Validation', { tags: ['@ui-selectors'] }, () => {
25
+ // ============================================
26
+ // NAVBAR (4 selectors)
27
+ // ============================================
28
+ describe('Public Navbar', () => {
29
+ beforeEach(() => {
30
+ // Visit home page (public, no login needed)
31
+ cy.visit('/', { timeout: 60000, failOnStatusCode: false })
32
+ })
33
+
34
+ it('should find navbar container', () => {
35
+ cy.get(PublicPagePOM.navbarSelectors.container).should('exist')
36
+ })
37
+
38
+ it('should find navbar logo', () => {
39
+ cy.get(PublicPagePOM.navbarSelectors.logo).should('exist')
40
+ })
41
+
42
+ it('should find login button (when not logged in)', () => {
43
+ cy.get(PublicPagePOM.navbarSelectors.loginButton).should('exist')
44
+ })
45
+
46
+ it('should find signup button (when not logged in)', () => {
47
+ cy.get(PublicPagePOM.navbarSelectors.signupButton).should('exist')
48
+ })
49
+ })
50
+
51
+ // ============================================
52
+ // FOOTER (2 selectors)
53
+ // ============================================
54
+ describe('Public Footer', () => {
55
+ beforeEach(() => {
56
+ cy.visit('/', { timeout: 60000, failOnStatusCode: false })
57
+ })
58
+
59
+ it('should find footer container', () => {
60
+ cy.get(PublicPagePOM.footerSelectors.container).should('exist')
61
+ })
62
+
63
+ it('should find footer logo', () => {
64
+ cy.get(PublicPagePOM.footerSelectors.logo).should('exist')
65
+ })
66
+ })
67
+
68
+ // ============================================
69
+ // PAGE SELECTORS (3 selectors)
70
+ // NOTE: Requires published pages to exist
71
+ // ============================================
72
+ describe('Public Page Selectors', () => {
73
+ // public.page.container - dynamic selector 'public-page-{slug}'
74
+ it.skip('should find page container (requires published page)', () => {
75
+ // Example: cy.visit('/about-us')
76
+ // cy.get('[data-cy="public-page-about-us"]').should('exist')
77
+ cy.wrap(true).should('be.true')
78
+ })
79
+
80
+ // public.page.title - 'page-title'
81
+ it.skip('should find page title (requires published page)', () => {
82
+ // cy.get('[data-cy="page-title"]').should('exist')
83
+ cy.wrap(true).should('be.true')
84
+ })
85
+
86
+ // public.page.content - 'page-content'
87
+ it.skip('should find page content (requires published page)', () => {
88
+ // cy.get('[data-cy="page-content"]').should('exist')
89
+ cy.wrap(true).should('be.true')
90
+ })
91
+ })
92
+
93
+ // ============================================
94
+ // BLOG SELECTORS (2 selectors)
95
+ // NOTE: Requires blog posts to exist
96
+ // ============================================
97
+ describe('Public Blog Selectors', () => {
98
+ // public.blog.listContainer - 'blog-list'
99
+ it.skip('should find blog list container (requires blog posts)', () => {
100
+ // cy.visit('/blog')
101
+ // cy.get('[data-cy="blog-list"]').should('exist')
102
+ cy.wrap(true).should('be.true')
103
+ })
104
+
105
+ // public.blog.postCard - dynamic selector 'blog-post-{slug}'
106
+ it.skip('should find blog post card (requires blog posts)', () => {
107
+ // cy.visit('/blog')
108
+ // cy.get('[data-cy="blog-post-my-first-post"]').should('exist')
109
+ cy.wrap(true).should('be.true')
110
+ })
111
+ })
112
+ })