@nextsparkjs/theme-default 0.1.0-beta.20 → 0.1.0-beta.21

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 (220) hide show
  1. package/package.json +1 -1
  2. package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
  3. package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
  4. package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
  5. package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
  6. package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
  7. package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
  8. package/tests/cypress/e2e/_docs/README.md +215 -0
  9. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
  10. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
  11. package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
  12. package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
  13. package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
  14. package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
  15. package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
  16. package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
  17. package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
  18. package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
  19. package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
  20. package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
  21. package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
  22. package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
  23. package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
  24. package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
  25. package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
  26. package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
  27. package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
  28. package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
  29. package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
  30. package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
  31. package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
  32. package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
  33. package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
  34. package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
  35. package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
  36. package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
  37. package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
  38. package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
  39. package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
  40. package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
  41. package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
  42. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  43. package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
  44. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  45. package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
  46. package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
  47. package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
  48. package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
  49. package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
  50. package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
  51. package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
  52. package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
  53. package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
  54. package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
  55. package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
  56. package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
  57. package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
  58. package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
  59. package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
  60. package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
  61. package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
  62. package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
  63. package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
  64. package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
  65. package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
  66. package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
  67. package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
  68. package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
  69. package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
  70. package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
  71. package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
  72. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
  73. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
  74. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  75. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  76. package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
  77. package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
  78. package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
  79. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
  80. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
  81. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
  82. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
  83. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
  84. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
  85. package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
  86. package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
  87. package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
  88. package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
  89. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
  90. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
  91. package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
  92. package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
  93. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
  94. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
  95. package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
  96. package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
  97. package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
  98. package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
  99. package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
  100. package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
  101. package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
  102. package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
  103. package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
  104. package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
  105. package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
  106. package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
  107. package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
  108. package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
  109. package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
  110. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  111. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  112. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  113. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  114. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  115. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  116. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  117. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  118. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  119. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  120. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  121. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  122. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  123. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  124. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  125. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  126. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  127. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  128. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  129. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  130. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  131. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  132. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  133. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  134. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  135. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  136. package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
  137. package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
  138. package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
  139. package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
  140. package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
  141. package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
  142. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
  143. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
  144. package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
  145. package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
  146. package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
  147. package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
  148. package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
  149. package/tests/cypress/fixtures/blocks.json +218 -0
  150. package/tests/cypress/fixtures/entities.json +78 -0
  151. package/tests/cypress/fixtures/page-builder.json +21 -0
  152. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  153. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  154. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  155. package/tests/cypress/src/components/EntityForm.ts +375 -0
  156. package/tests/cypress/src/components/EntityList.ts +389 -0
  157. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  158. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  159. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  160. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  161. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  162. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  163. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  164. package/tests/cypress/src/components/index.ts +21 -0
  165. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  166. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  167. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  168. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  169. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  170. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  171. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  172. package/tests/cypress/src/controllers/index.js +25 -0
  173. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  174. package/tests/cypress/src/core/BasePOM.ts +86 -0
  175. package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
  176. package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
  177. package/tests/cypress/src/core/index.ts +14 -0
  178. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  179. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  180. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  181. package/tests/cypress/src/entities/TasksPOM.ts +176 -0
  182. package/tests/cypress/src/entities/index.ts +14 -0
  183. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  184. package/tests/cypress/src/features/DashboardPOM.ts +245 -0
  185. package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
  186. package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
  187. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  188. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  189. package/tests/cypress/src/features/SettingsPOM.ts +362 -0
  190. package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
  191. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  192. package/tests/cypress/src/features/index.ts +28 -0
  193. package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
  194. package/tests/cypress/src/index.ts +101 -0
  195. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  196. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  197. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  198. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  199. package/tests/cypress/src/pages/index.js +5 -0
  200. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  201. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  202. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  203. package/tests/cypress/src/pages/public/index.js +6 -0
  204. package/tests/cypress/src/selectors.ts +46 -0
  205. package/tests/cypress/src/session-helpers.ts +500 -0
  206. package/tests/cypress/support/doc-commands.ts +260 -0
  207. package/tests/cypress.config.ts +150 -0
  208. package/tests/jest/components/post-header.test.tsx +377 -0
  209. package/tests/jest/config/role-config.test.ts +529 -0
  210. package/tests/jest/jest.config.ts +81 -0
  211. package/tests/jest/langchain/COVERAGE.md +372 -0
  212. package/tests/jest/langchain/guardrails.test.ts +465 -0
  213. package/tests/jest/langchain/streaming.test.ts +367 -0
  214. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  215. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  216. package/tests/jest/langchain/tracer.test.ts +823 -0
  217. package/tests/jest/user-roles/role-helpers.test.ts +432 -0
  218. package/tests/jest/validation/categories.test.ts +429 -0
  219. package/tests/jest/validation/posts.test.ts +546 -0
  220. package/tests/tsconfig.json +15 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * BasePOM - Base class with common utilities for all Page Object Models
3
+ *
4
+ * Provides:
5
+ * - Selector pattern replacement for {placeholder} patterns
6
+ * - Wrapper methods for common Cypress operations
7
+ * - Fluent interface support (methods return `this`)
8
+ */
9
+ import { cySelector as cySel, type Replacements } from '../selectors'
10
+
11
+ export abstract class BasePOM {
12
+ /**
13
+ * Replaces placeholders in a selector pattern and wraps with data-cy attribute
14
+ * @deprecated Use cySelector() from selectors.ts instead
15
+ * @param pattern - Selector pattern with {placeholder} syntax
16
+ * @param replacements - Object with placeholder values
17
+ * @returns Formatted data-cy selector string
18
+ *
19
+ * @example
20
+ * selector('{slug}-row-{id}', { slug: 'tasks', id: '123' })
21
+ * // Returns: '[data-cy="tasks-row-123"]'
22
+ */
23
+ protected selector(pattern: string, replacements: Record<string, string> = {}): string {
24
+ let result = pattern
25
+ for (const [key, value] of Object.entries(replacements)) {
26
+ result = result.replaceAll(`{${key}}`, value)
27
+ }
28
+ return `[data-cy="${result}"]`
29
+ }
30
+
31
+ /**
32
+ * Get a Cypress selector using the centralized selectors
33
+ * @param path - Dot-notation path to the selector (e.g., "auth.login.form")
34
+ * @param replacements - Optional placeholder replacements
35
+ * @returns Cypress selector string like [data-cy="selector-value"]
36
+ *
37
+ * @example
38
+ * this.cy('auth.login.form')
39
+ * // Returns: '[data-cy="login-form"]'
40
+ *
41
+ * this.cy('entities.table.row', { slug: 'tasks', id: '123' })
42
+ * // Returns: '[data-cy="tasks-row-123"]'
43
+ */
44
+ protected cy(path: string, replacements?: Replacements): string {
45
+ return cySel(path, replacements)
46
+ }
47
+
48
+ /**
49
+ * Wrapper for cy.get with selector pattern support
50
+ * @param pattern - Selector pattern or direct selector
51
+ * @param replacements - Optional placeholder replacements
52
+ */
53
+ protected get(pattern: string, replacements: Record<string, string> = {}) {
54
+ return cy.get(this.selector(pattern, replacements))
55
+ }
56
+
57
+ /**
58
+ * Generic wait with configurable timeout
59
+ * @param selector - CSS selector to wait for
60
+ * @param timeout - Max wait time in ms (default: 15000)
61
+ */
62
+ protected waitFor(selector: string, timeout = 15000) {
63
+ cy.get(selector, { timeout }).should('be.visible')
64
+ return this
65
+ }
66
+
67
+ /**
68
+ * Wait for URL to contain a specific path
69
+ * @param path - Path segment to check for
70
+ */
71
+ protected waitForUrl(path: string) {
72
+ cy.url().should('include', path)
73
+ return this
74
+ }
75
+
76
+ /**
77
+ * Wait for URL to match a regex pattern
78
+ * @param pattern - RegExp to match against URL
79
+ */
80
+ protected waitForUrlMatch(pattern: RegExp) {
81
+ cy.url().should('match', pattern)
82
+ return this
83
+ }
84
+ }
85
+
86
+ export default BasePOM
@@ -0,0 +1,576 @@
1
+ /**
2
+ * BlockEditorBasePOM - Base class for Page and Post builder POMs
3
+ *
4
+ * Provides common functionality for the block-based editor:
5
+ * - Editor navigation and setup
6
+ * - Title and slug management
7
+ * - Block picker interactions
8
+ * - Block manipulation (add, remove, reorder)
9
+ * - Settings panel interactions
10
+ * - Status management
11
+ * - Save operations
12
+ *
13
+ * @example
14
+ * class PageBuilderPOM extends BlockEditorBasePOM {
15
+ * protected entitySlug = 'pages'
16
+ *
17
+ * publishPage() {
18
+ * this.setStatus('published')
19
+ * this.save()
20
+ * return this
21
+ * }
22
+ * }
23
+ */
24
+
25
+ import { BasePOM } from './BasePOM'
26
+ import { cySelector } from '../selectors'
27
+ import { ApiInterceptor } from '../helpers/ApiInterceptor'
28
+
29
+ export abstract class BlockEditorBasePOM extends BasePOM {
30
+ /**
31
+ * Entity slug - must be defined by subclass
32
+ */
33
+ protected abstract entitySlug: string
34
+
35
+ protected _api: ApiInterceptor | null = null
36
+
37
+ // ============================================
38
+ // API INTERCEPTOR
39
+ // ============================================
40
+
41
+ get api(): ApiInterceptor {
42
+ if (!this._api) {
43
+ this._api = new ApiInterceptor(this.entitySlug)
44
+ }
45
+ return this._api
46
+ }
47
+
48
+ setupApiIntercepts() {
49
+ this.api.setupCrudIntercepts()
50
+ return this
51
+ }
52
+
53
+ // ============================================
54
+ // SELECTORS (from centralized selectors.ts)
55
+ // ============================================
56
+
57
+ get editorSelectors() {
58
+ return {
59
+ // Editor main
60
+ container: cySelector('blockEditor.container'),
61
+ titleInput: cySelector('blockEditor.titleInput'),
62
+ slugInput: cySelector('blockEditor.slugInput'),
63
+ saveButton: cySelector('blockEditor.saveButton'),
64
+ statusBadge: cySelector('blockEditor.statusBadge'),
65
+ leftSidebarToggle: cySelector('blockEditor.leftSidebarToggle'),
66
+ viewModeToggle: cySelector('blockEditor.viewModeToggle'),
67
+
68
+ // Block Picker
69
+ blockPicker: cySelector('blockEditor.blockPicker.container'),
70
+ blockSearch: cySelector('blockEditor.blockPicker.searchInput'),
71
+ categoryAll: cySelector('blockEditor.blockPicker.categoryAll'),
72
+ category: (category: string) =>
73
+ cySelector('blockEditor.blockPicker.category', { category }),
74
+ blockItem: (slug: string) => cySelector('blockEditor.blockPicker.blockItem', { slug }),
75
+ addBlock: (slug: string) => cySelector('blockEditor.blockPicker.addBlock', { slug }),
76
+
77
+ // Block Canvas
78
+ blockCanvas: cySelector('blockEditor.blockCanvas.container'),
79
+ blockCanvasEmpty: cySelector('blockEditor.blockCanvas.empty'),
80
+
81
+ // Preview Canvas
82
+ previewCanvas: cySelector('blockEditor.previewCanvas.container'),
83
+ previewCanvasEmpty: cySelector('blockEditor.previewCanvas.empty'),
84
+ previewBlock: (id: string) => cySelector('blockEditor.previewCanvas.block', { id }),
85
+ previewMoveUp: (id: string) => cySelector('blockEditor.previewCanvas.moveUp', { id }),
86
+ previewMoveDown: (id: string) => cySelector('blockEditor.previewCanvas.moveDown', { id }),
87
+
88
+ // Sortable Block
89
+ sortableBlock: (id: string) => cySelector('blockEditor.sortableBlock.container', { id }),
90
+ dragHandle: (id: string) => cySelector('blockEditor.sortableBlock.dragHandle', { id }),
91
+ duplicateBlock: (id: string) => cySelector('blockEditor.sortableBlock.duplicate', { id }),
92
+ removeBlock: (id: string) => cySelector('blockEditor.sortableBlock.remove', { id }),
93
+ blockError: (id: string) => cySelector('blockEditor.sortableBlock.error', { id }),
94
+
95
+ // Settings Panel
96
+ settingsPanel: cySelector('blockEditor.settingsPanel.container'),
97
+ settingsPanelEmpty: cySelector('blockEditor.settingsPanel.empty'),
98
+ settingsPanelError: cySelector('blockEditor.settingsPanel.error'),
99
+ resetProps: cySelector('blockEditor.settingsPanel.resetProps'),
100
+ removeBlockSettings: cySelector('blockEditor.settingsPanel.removeBlock'),
101
+ tabContent: cySelector('blockEditor.settingsPanel.tabContent'),
102
+ tabDesign: cySelector('blockEditor.settingsPanel.tabDesign'),
103
+ tabAdvanced: cySelector('blockEditor.settingsPanel.tabAdvanced'),
104
+
105
+ // Page Settings
106
+ pageSettings: cySelector('blockEditor.pageSettings.container'),
107
+ seoTrigger: cySelector('blockEditor.pageSettings.seoTrigger'),
108
+ metaTitle: cySelector('blockEditor.pageSettings.metaTitle'),
109
+ metaDescription: cySelector('blockEditor.pageSettings.metaDescription'),
110
+ metaKeywords: cySelector('blockEditor.pageSettings.metaKeywords'),
111
+ ogImage: cySelector('blockEditor.pageSettings.ogImage'),
112
+ customFieldsTrigger: cySelector('blockEditor.pageSettings.customFieldsTrigger'),
113
+ customFieldKey: (index: number) =>
114
+ cySelector('blockEditor.pageSettings.customFieldKey', { index }),
115
+ customFieldValue: (index: number) =>
116
+ cySelector('blockEditor.pageSettings.customFieldValue', { index }),
117
+ customFieldRemove: (index: number) =>
118
+ cySelector('blockEditor.pageSettings.customFieldRemove', { index }),
119
+ addCustomField: cySelector('blockEditor.pageSettings.addCustomField'),
120
+
121
+ // Status Selector
122
+ statusSelector: cySelector('blockEditor.statusSelector.trigger'),
123
+ statusOption: (value: string) =>
124
+ cySelector('blockEditor.statusSelector.option', { value }),
125
+
126
+ // Dynamic Form
127
+ dynamicForm: cySelector('blockEditor.dynamicForm.container'),
128
+ dynamicField: (name: string) =>
129
+ cySelector('blockEditor.dynamicForm.field', { name }),
130
+ fieldGroup: (id: string) =>
131
+ cySelector('blockEditor.dynamicForm.fieldGroup', { id }),
132
+ arrayGroup: (name: string) =>
133
+ cySelector('blockEditor.dynamicForm.arrayGroup', { name }),
134
+
135
+ // Array Field
136
+ arrayFieldContainer: (name: string) =>
137
+ cySelector('blockEditor.arrayField.container', { name }),
138
+ arrayFieldItem: (name: string, index: number, field: string) =>
139
+ cySelector('blockEditor.arrayField.item', { name, index, field }),
140
+ arrayFieldMoveUp: (name: string, index: number) =>
141
+ cySelector('blockEditor.arrayField.moveUp', { name, index }),
142
+ arrayFieldMoveDown: (name: string, index: number) =>
143
+ cySelector('blockEditor.arrayField.moveDown', { name, index }),
144
+ arrayFieldRemove: (name: string, index: number) =>
145
+ cySelector('blockEditor.arrayField.remove', { name, index }),
146
+ arrayFieldAdd: (name: string) =>
147
+ cySelector('blockEditor.arrayField.add', { name }),
148
+
149
+ // Entity Fields Sidebar
150
+ entityFieldsSidebar: cySelector('blockEditor.entityFieldsSidebar.container'),
151
+ entityField: (name: string) =>
152
+ cySelector('blockEditor.entityFieldsSidebar.field', { name }),
153
+ entityCategory: (slug: string) =>
154
+ cySelector('blockEditor.entityFieldsSidebar.category', { slug })
155
+ }
156
+ }
157
+
158
+ // ============================================
159
+ // NAVIGATION
160
+ // ============================================
161
+
162
+ visitList() {
163
+ cy.visit(`/dashboard/${this.entitySlug}`)
164
+ return this
165
+ }
166
+
167
+ visitCreate() {
168
+ cy.visit(`/dashboard/${this.entitySlug}/create`)
169
+ return this
170
+ }
171
+
172
+ visitEdit(id: string) {
173
+ cy.visit(`/dashboard/${this.entitySlug}/${id}/edit`)
174
+ return this
175
+ }
176
+
177
+ // ============================================
178
+ // WAITS
179
+ // ============================================
180
+
181
+ waitForEditor() {
182
+ cy.get(this.editorSelectors.container, { timeout: 15000 }).should('be.visible')
183
+ return this
184
+ }
185
+
186
+ waitForSave() {
187
+ this.api.waitForUpdate()
188
+ return this
189
+ }
190
+
191
+ // ============================================
192
+ // EDITOR ACTIONS
193
+ // ============================================
194
+
195
+ /**
196
+ * Set the page/post title
197
+ */
198
+ setTitle(title: string) {
199
+ cy.get(this.editorSelectors.titleInput).clear().type(title)
200
+ return this
201
+ }
202
+
203
+ /**
204
+ * Set the page/post slug
205
+ */
206
+ setSlug(slug: string) {
207
+ cy.get(this.editorSelectors.slugInput).clear().type(slug)
208
+ return this
209
+ }
210
+
211
+ /**
212
+ * Save the page/post
213
+ */
214
+ save() {
215
+ cy.get(this.editorSelectors.saveButton).click()
216
+ return this
217
+ }
218
+
219
+ /**
220
+ * Save with API wait
221
+ */
222
+ saveWithWait() {
223
+ this.setupApiIntercepts()
224
+ this.save()
225
+ this.api.waitForUpdate()
226
+ return this
227
+ }
228
+
229
+ /**
230
+ * Set the status using the status selector
231
+ */
232
+ setStatus(status: string) {
233
+ cy.get(this.editorSelectors.statusSelector).click()
234
+ cy.get(this.editorSelectors.statusOption(status)).click()
235
+ return this
236
+ }
237
+
238
+ /**
239
+ * Toggle left sidebar
240
+ */
241
+ toggleLeftSidebar() {
242
+ cy.get(this.editorSelectors.leftSidebarToggle).click()
243
+ return this
244
+ }
245
+
246
+ /**
247
+ * Toggle view mode (edit/preview)
248
+ */
249
+ toggleViewMode() {
250
+ cy.get(this.editorSelectors.viewModeToggle).click()
251
+ return this
252
+ }
253
+
254
+ // ============================================
255
+ // BLOCK PICKER ACTIONS
256
+ // ============================================
257
+
258
+ /**
259
+ * Search for a block in the picker
260
+ */
261
+ searchBlock(term: string) {
262
+ cy.get(this.editorSelectors.blockSearch).clear().type(term)
263
+ return this
264
+ }
265
+
266
+ /**
267
+ * Clear block search
268
+ */
269
+ clearBlockSearch() {
270
+ cy.get(this.editorSelectors.blockSearch).clear()
271
+ return this
272
+ }
273
+
274
+ /**
275
+ * Select a block category
276
+ */
277
+ selectCategory(category: string) {
278
+ cy.get(this.editorSelectors.category(category)).click()
279
+ return this
280
+ }
281
+
282
+ /**
283
+ * Select "All" category
284
+ */
285
+ selectAllCategories() {
286
+ cy.get(this.editorSelectors.categoryAll).click()
287
+ return this
288
+ }
289
+
290
+ /**
291
+ * Add a block by clicking its add button
292
+ */
293
+ addBlock(blockSlug: string) {
294
+ cy.get(this.editorSelectors.addBlock(blockSlug)).click()
295
+ return this
296
+ }
297
+
298
+ /**
299
+ * Click on a block item in the picker
300
+ */
301
+ clickBlockItem(blockSlug: string) {
302
+ cy.get(this.editorSelectors.blockItem(blockSlug)).click()
303
+ return this
304
+ }
305
+
306
+ // ============================================
307
+ // BLOCK MANIPULATION
308
+ // ============================================
309
+
310
+ /**
311
+ * Remove a block from the canvas
312
+ */
313
+ removeBlock(blockId: string) {
314
+ cy.get(this.editorSelectors.removeBlock(blockId)).click()
315
+ return this
316
+ }
317
+
318
+ /**
319
+ * Duplicate a block
320
+ */
321
+ duplicateBlock(blockId: string) {
322
+ cy.get(this.editorSelectors.duplicateBlock(blockId)).click()
323
+ return this
324
+ }
325
+
326
+ /**
327
+ * Click on a sortable block to select it
328
+ */
329
+ selectBlock(blockId: string) {
330
+ cy.get(this.editorSelectors.sortableBlock(blockId)).click()
331
+ return this
332
+ }
333
+
334
+ /**
335
+ * Move block up in preview
336
+ */
337
+ moveBlockUp(blockId: string) {
338
+ cy.get(this.editorSelectors.previewMoveUp(blockId)).click()
339
+ return this
340
+ }
341
+
342
+ /**
343
+ * Move block down in preview
344
+ */
345
+ moveBlockDown(blockId: string) {
346
+ cy.get(this.editorSelectors.previewMoveDown(blockId)).click()
347
+ return this
348
+ }
349
+
350
+ // ============================================
351
+ // SETTINGS PANEL ACTIONS
352
+ // ============================================
353
+
354
+ /**
355
+ * Switch to Content tab in settings panel
356
+ */
357
+ openContentTab() {
358
+ cy.get(this.editorSelectors.tabContent).click()
359
+ return this
360
+ }
361
+
362
+ /**
363
+ * Switch to Design tab in settings panel
364
+ */
365
+ openDesignTab() {
366
+ cy.get(this.editorSelectors.tabDesign).click()
367
+ return this
368
+ }
369
+
370
+ /**
371
+ * Switch to Advanced tab in settings panel
372
+ */
373
+ openAdvancedTab() {
374
+ cy.get(this.editorSelectors.tabAdvanced).click()
375
+ return this
376
+ }
377
+
378
+ /**
379
+ * Reset block props to defaults
380
+ */
381
+ resetBlockProps() {
382
+ cy.get(this.editorSelectors.resetProps).click()
383
+ return this
384
+ }
385
+
386
+ /**
387
+ * Remove block from settings panel
388
+ */
389
+ removeBlockFromSettings() {
390
+ cy.get(this.editorSelectors.removeBlockSettings).click()
391
+ return this
392
+ }
393
+
394
+ /**
395
+ * Fill a dynamic form field
396
+ */
397
+ fillDynamicField(name: string, value: string) {
398
+ cy.get(this.editorSelectors.dynamicField(name)).find('input, textarea').clear().type(value)
399
+ return this
400
+ }
401
+
402
+ // ============================================
403
+ // SEO/PAGE SETTINGS
404
+ // ============================================
405
+
406
+ /**
407
+ * Open SEO settings section
408
+ */
409
+ openSeoSettings() {
410
+ cy.get(this.editorSelectors.seoTrigger).click()
411
+ return this
412
+ }
413
+
414
+ /**
415
+ * Set meta title
416
+ */
417
+ setMetaTitle(title: string) {
418
+ cy.get(this.editorSelectors.metaTitle).clear().type(title)
419
+ return this
420
+ }
421
+
422
+ /**
423
+ * Set meta description
424
+ */
425
+ setMetaDescription(description: string) {
426
+ cy.get(this.editorSelectors.metaDescription).clear().type(description)
427
+ return this
428
+ }
429
+
430
+ /**
431
+ * Set meta keywords
432
+ */
433
+ setMetaKeywords(keywords: string) {
434
+ cy.get(this.editorSelectors.metaKeywords).clear().type(keywords)
435
+ return this
436
+ }
437
+
438
+ /**
439
+ * Open custom fields section
440
+ */
441
+ openCustomFields() {
442
+ cy.get(this.editorSelectors.customFieldsTrigger).click()
443
+ return this
444
+ }
445
+
446
+ /**
447
+ * Add a custom field
448
+ */
449
+ addCustomField() {
450
+ cy.get(this.editorSelectors.addCustomField).click()
451
+ return this
452
+ }
453
+
454
+ /**
455
+ * Fill a custom field
456
+ */
457
+ fillCustomField(index: number, key: string, value: string) {
458
+ cy.get(this.editorSelectors.customFieldKey(index)).clear().type(key)
459
+ cy.get(this.editorSelectors.customFieldValue(index)).clear().type(value)
460
+ return this
461
+ }
462
+
463
+ /**
464
+ * Remove a custom field
465
+ */
466
+ removeCustomField(index: number) {
467
+ cy.get(this.editorSelectors.customFieldRemove(index)).click()
468
+ return this
469
+ }
470
+
471
+ // ============================================
472
+ // ARRAY FIELDS
473
+ // ============================================
474
+
475
+ /**
476
+ * Add an item to an array field
477
+ */
478
+ addArrayItem(name: string) {
479
+ cy.get(this.editorSelectors.arrayFieldAdd(name)).click()
480
+ return this
481
+ }
482
+
483
+ /**
484
+ * Remove an item from an array field
485
+ */
486
+ removeArrayItem(name: string, index: number) {
487
+ cy.get(this.editorSelectors.arrayFieldRemove(name, index)).click()
488
+ return this
489
+ }
490
+
491
+ /**
492
+ * Fill an array field item
493
+ */
494
+ fillArrayItem(name: string, index: number, field: string, value: string) {
495
+ cy.get(this.editorSelectors.arrayFieldItem(name, index, field)).clear().type(value)
496
+ return this
497
+ }
498
+
499
+ /**
500
+ * Move array item up
501
+ */
502
+ moveArrayItemUp(name: string, index: number) {
503
+ cy.get(this.editorSelectors.arrayFieldMoveUp(name, index)).click()
504
+ return this
505
+ }
506
+
507
+ /**
508
+ * Move array item down
509
+ */
510
+ moveArrayItemDown(name: string, index: number) {
511
+ cy.get(this.editorSelectors.arrayFieldMoveDown(name, index)).click()
512
+ return this
513
+ }
514
+
515
+ // ============================================
516
+ // ASSERTIONS
517
+ // ============================================
518
+
519
+ /**
520
+ * Assert editor is visible
521
+ */
522
+ assertEditorVisible() {
523
+ cy.get(this.editorSelectors.container).should('be.visible')
524
+ return this
525
+ }
526
+
527
+ /**
528
+ * Assert canvas is empty
529
+ */
530
+ assertCanvasEmpty() {
531
+ cy.get(this.editorSelectors.blockCanvasEmpty).should('be.visible')
532
+ return this
533
+ }
534
+
535
+ /**
536
+ * Assert block exists in canvas
537
+ */
538
+ assertBlockExists(blockId: string) {
539
+ cy.get(this.editorSelectors.sortableBlock(blockId)).should('exist')
540
+ return this
541
+ }
542
+
543
+ /**
544
+ * Assert block does not exist
545
+ */
546
+ assertBlockNotExists(blockId: string) {
547
+ cy.get(this.editorSelectors.sortableBlock(blockId)).should('not.exist')
548
+ return this
549
+ }
550
+
551
+ /**
552
+ * Assert status badge shows specific status
553
+ */
554
+ assertStatus(status: string) {
555
+ cy.get(this.editorSelectors.statusBadge).should('contain.text', status)
556
+ return this
557
+ }
558
+
559
+ /**
560
+ * Assert settings panel is visible
561
+ */
562
+ assertSettingsPanelVisible() {
563
+ cy.get(this.editorSelectors.settingsPanel).should('be.visible')
564
+ return this
565
+ }
566
+
567
+ /**
568
+ * Assert settings panel shows empty state
569
+ */
570
+ assertSettingsPanelEmpty() {
571
+ cy.get(this.editorSelectors.settingsPanelEmpty).should('be.visible')
572
+ return this
573
+ }
574
+ }
575
+
576
+ export default BlockEditorBasePOM