@nextsparkjs/theme-default 0.1.0-beta.2 → 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 (222) hide show
  1. package/package.json +8 -4
  2. package/templates/(public)/page.tsx +1 -1
  3. package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
  4. package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
  5. package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
  6. package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
  7. package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
  8. package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
  9. package/tests/cypress/e2e/_docs/README.md +215 -0
  10. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
  11. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
  12. package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
  13. package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
  14. package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
  15. package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
  16. package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
  17. package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
  18. package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
  19. package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
  20. package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
  21. package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
  22. package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
  23. package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
  24. package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
  25. package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
  26. package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
  27. package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
  28. package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
  29. package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
  30. package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
  31. package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
  32. package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
  33. package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
  34. package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
  35. package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
  36. package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
  37. package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
  38. package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
  39. package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
  40. package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
  41. package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
  42. package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
  43. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  44. package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
  45. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  46. package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
  47. package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
  48. package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
  49. package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
  50. package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
  51. package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
  52. package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
  53. package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
  54. package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
  55. package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
  56. package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
  57. package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
  58. package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
  59. package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
  60. package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
  61. package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
  62. package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
  63. package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
  64. package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
  65. package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
  66. package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
  67. package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
  68. package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
  69. package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
  70. package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
  71. package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
  72. package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
  73. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
  74. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
  75. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  76. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  77. package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
  78. package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
  79. package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
  80. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
  81. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
  82. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
  83. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
  84. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
  85. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
  86. package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
  87. package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
  88. package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
  89. package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
  90. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
  91. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
  92. package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
  93. package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
  94. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
  95. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
  96. package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
  97. package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
  98. package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
  99. package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
  100. package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
  101. package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
  102. package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
  103. package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
  104. package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
  105. package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
  106. package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
  107. package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
  108. package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
  109. package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
  110. package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
  111. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  112. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  113. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  114. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  115. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  116. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  117. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  118. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  119. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  120. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  121. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  122. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  123. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  124. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  125. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  126. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  127. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  128. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  129. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  130. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  131. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  132. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  133. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  134. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  135. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  136. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  137. package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
  138. package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
  139. package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
  140. package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
  141. package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
  142. package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
  143. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
  144. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
  145. package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
  146. package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
  147. package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
  148. package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
  149. package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
  150. package/tests/cypress/fixtures/blocks.json +218 -0
  151. package/tests/cypress/fixtures/entities.json +78 -0
  152. package/tests/cypress/fixtures/page-builder.json +21 -0
  153. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  154. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  155. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  156. package/tests/cypress/src/components/EntityForm.ts +375 -0
  157. package/tests/cypress/src/components/EntityList.ts +389 -0
  158. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  159. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  160. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  161. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  162. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  163. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  164. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  165. package/tests/cypress/src/components/index.ts +21 -0
  166. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  167. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  168. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  169. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  170. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  171. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  172. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  173. package/tests/cypress/src/controllers/index.js +25 -0
  174. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  175. package/tests/cypress/src/core/BasePOM.ts +86 -0
  176. package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
  177. package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
  178. package/tests/cypress/src/core/index.ts +14 -0
  179. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  180. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  181. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  182. package/tests/cypress/src/entities/TasksPOM.ts +176 -0
  183. package/tests/cypress/src/entities/index.ts +14 -0
  184. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  185. package/tests/cypress/src/features/DashboardPOM.ts +245 -0
  186. package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
  187. package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
  188. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  189. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  190. package/tests/cypress/src/features/SettingsPOM.ts +362 -0
  191. package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
  192. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  193. package/tests/cypress/src/features/index.ts +28 -0
  194. package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
  195. package/tests/cypress/src/index.ts +101 -0
  196. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  197. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  198. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  199. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  200. package/tests/cypress/src/pages/index.js +5 -0
  201. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  202. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  203. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  204. package/tests/cypress/src/pages/public/index.js +6 -0
  205. package/tests/cypress/src/selectors.ts +46 -0
  206. package/tests/cypress/src/session-helpers.ts +500 -0
  207. package/tests/cypress/support/doc-commands.ts +260 -0
  208. package/tests/cypress.config.ts +150 -0
  209. package/tests/jest/components/post-header.test.tsx +377 -0
  210. package/tests/jest/config/role-config.test.ts +529 -0
  211. package/tests/jest/jest.config.ts +81 -0
  212. package/tests/jest/langchain/COVERAGE.md +372 -0
  213. package/tests/jest/langchain/guardrails.test.ts +465 -0
  214. package/tests/jest/langchain/streaming.test.ts +367 -0
  215. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  216. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  217. package/tests/jest/langchain/tracer.test.ts +823 -0
  218. package/tests/jest/user-roles/role-helpers.test.ts +432 -0
  219. package/tests/jest/validation/categories.test.ts +429 -0
  220. package/tests/jest/validation/posts.test.ts +546 -0
  221. package/tests/tsconfig.json +15 -0
  222. package/LICENSE +0 -21
@@ -0,0 +1,692 @@
1
+ /**
2
+ * DashboardEntityPOM - Base class for all entity Page Object Models
3
+ *
4
+ * Provides standard CRUD operations for dashboard entities:
5
+ * - Navigation (list, create, edit, detail pages)
6
+ * - Table interactions (search, filters, pagination, row actions)
7
+ * - Form operations (fill fields, submit, cancel)
8
+ * - API interceptor integration for deterministic waits
9
+ * - Bulk actions
10
+ * - Delete confirmation dialogs
11
+ *
12
+ * Entity POMs should extend this class and add entity-specific methods.
13
+ *
14
+ * @example
15
+ * class TasksPOM extends DashboardEntityPOM {
16
+ * constructor() {
17
+ * super('tasks')
18
+ * }
19
+ *
20
+ * fillTaskForm(data: TaskFormData) {
21
+ * // Entity-specific form handling
22
+ * }
23
+ * }
24
+ */
25
+
26
+ import { BasePOM } from './BasePOM'
27
+ import { cySelector } from '../selectors'
28
+ import entitiesConfig from '../../fixtures/entities.json'
29
+ import { ApiInterceptor } from '../helpers/ApiInterceptor'
30
+
31
+ export interface EntityConfig {
32
+ slug: string
33
+ singular: string
34
+ plural: string
35
+ tableName: string
36
+ fields: string[]
37
+ filters: string[]
38
+ }
39
+
40
+ export abstract class DashboardEntityPOM extends BasePOM {
41
+ protected slug: string
42
+ protected entityConfig: EntityConfig
43
+ protected _api: ApiInterceptor | null = null
44
+
45
+ /**
46
+ * Get the entity slug (public accessor)
47
+ * Useful for building dynamic selectors and URLs in tests
48
+ */
49
+ get entitySlug(): string {
50
+ return this.slug
51
+ }
52
+
53
+ constructor(entitySlug: string) {
54
+ super()
55
+ // Find entity config by slug
56
+ const entry = Object.entries(entitiesConfig.entities).find(([, config]) => config.slug === entitySlug)
57
+ if (!entry) {
58
+ throw new Error(
59
+ `Unknown entity slug: ${entitySlug}. Available: ${Object.values(entitiesConfig.entities).map(e => e.slug).join(', ')}`
60
+ )
61
+ }
62
+ this.entityConfig = entry[1] as EntityConfig
63
+ this.slug = entitySlug
64
+ }
65
+
66
+ // ============================================
67
+ // API INTERCEPTOR
68
+ // ============================================
69
+
70
+ /**
71
+ * Get or create ApiInterceptor instance for this entity
72
+ */
73
+ get api(): ApiInterceptor {
74
+ if (!this._api) {
75
+ this._api = new ApiInterceptor(this.slug)
76
+ }
77
+ return this._api
78
+ }
79
+
80
+ /**
81
+ * Setup API intercepts for all CRUD operations
82
+ * Call this BEFORE navigation
83
+ */
84
+ setupApiIntercepts() {
85
+ this.api.setupCrudIntercepts()
86
+ return this
87
+ }
88
+
89
+ // ============================================
90
+ // SELECTORS (from centralized selectors.ts)
91
+ // ============================================
92
+
93
+ /**
94
+ * Get all selectors for this entity, with placeholders replaced
95
+ */
96
+ get selectors() {
97
+ const slug = this.slug
98
+
99
+ return {
100
+ // Page
101
+ page: cySelector('entities.page.container', { slug }),
102
+ pageTitle: cySelector('entities.page.title', { slug }),
103
+
104
+ // Table
105
+ tableContainer: cySelector('entities.table.container', { slug }),
106
+ table: cySelector('entities.table.element', { slug }),
107
+ addButton: cySelector('entities.table.addButton', { slug }),
108
+ search: cySelector('entities.search.input', { slug }),
109
+ searchContainer: cySelector('entities.search.container', { slug }),
110
+ searchClear: cySelector('entities.search.clear', { slug }),
111
+ selectAll: cySelector('entities.table.selectAll', { slug }),
112
+ selectionCount: cySelector('entities.table.selectionCount', { slug }),
113
+ row: (id: string) => cySelector('entities.table.row', { slug, id }),
114
+ rowSelect: (id: string) => cySelector('entities.table.rowSelect', { slug, id }),
115
+ rowMenu: (id: string) => cySelector('entities.table.rowMenu', { slug, id }),
116
+ rowAction: (action: string, id: string) => cySelector('entities.table.rowAction', { slug, action, id }),
117
+ cell: (field: string, id: string) => cySelector('entities.table.cell', { slug, field, id }),
118
+ rowGeneric: `[data-cy^="${slug}-row-"]`,
119
+
120
+ // Pagination
121
+ pagination: cySelector('entities.pagination.container', { slug }),
122
+ pageSize: cySelector('entities.pagination.pageSize', { slug }),
123
+ pageSizeOption: (size: string) => cySelector('entities.pagination.pageSizeOption', { slug, size }),
124
+ pageInfo: cySelector('entities.pagination.pageInfo', { slug }),
125
+ pageFirst: cySelector('entities.pagination.first', { slug }),
126
+ pagePrev: cySelector('entities.pagination.prev', { slug }),
127
+ pageNext: cySelector('entities.pagination.next', { slug }),
128
+ pageLast: cySelector('entities.pagination.last', { slug }),
129
+
130
+ // Header (detail pages) - modes: view, edit, create
131
+ viewHeader: cySelector('entities.header.container', { slug, mode: 'view' }),
132
+ editHeader: cySelector('entities.header.container', { slug, mode: 'edit' }),
133
+ createHeader: cySelector('entities.header.container', { slug, mode: 'create' }),
134
+ backButton: cySelector('entities.header.backButton', { slug }),
135
+ editButton: cySelector('entities.header.editButton', { slug }),
136
+ deleteButton: cySelector('entities.header.deleteButton', { slug }),
137
+ copyId: cySelector('entities.header.copyId', { slug }),
138
+ title: cySelector('entities.header.title', { slug }),
139
+
140
+ // Delete confirmation
141
+ deleteDialog: cySelector('entities.header.deleteDialog', { slug }),
142
+ deleteCancel: cySelector('entities.header.deleteCancel', { slug }),
143
+ deleteConfirm: cySelector('entities.header.deleteConfirm', { slug }),
144
+
145
+ // Form
146
+ form: cySelector('entities.form.container', { slug }),
147
+ field: (name: string) => cySelector('entities.form.field', { slug, name }),
148
+ submitButton: cySelector('entities.form.submitButton', { slug }),
149
+
150
+ // Filters
151
+ filter: (field: string) => cySelector('entities.filter.container', { slug, field }),
152
+ filterTrigger: (field: string) => cySelector('entities.filter.trigger', { slug, field }),
153
+ filterContent: (field: string) => cySelector('entities.filter.content', { slug, field }),
154
+ filterOption: (field: string, value: string) => cySelector('entities.filter.option', { slug, field, value }),
155
+ filterBadge: (field: string, value: string) => cySelector('entities.filter.badge', { slug, field, value }),
156
+ filterRemoveBadge: (field: string, value: string) =>
157
+ cySelector('entities.filter.removeBadge', { slug, field, value }),
158
+ filterClearAll: (field: string) => cySelector('entities.filter.clearAll', { slug, field }),
159
+
160
+ // Bulk actions
161
+ bulkBar: cySelector('entities.bulk.bar', { slug }),
162
+ bulkCount: cySelector('entities.bulk.count', { slug }),
163
+ bulkSelectAll: cySelector('entities.bulk.selectAll', { slug }),
164
+ bulkStatus: cySelector('entities.bulk.statusButton', { slug }),
165
+ bulkDelete: cySelector('entities.bulk.deleteButton', { slug }),
166
+ bulkClear: cySelector('entities.bulk.clearButton', { slug }),
167
+
168
+ // Bulk status dialog
169
+ bulkStatusDialog: cySelector('entities.bulk.statusDialog', { slug }),
170
+ bulkStatusSelect: cySelector('entities.bulk.statusSelect', { slug }),
171
+ bulkStatusOption: (value: string) => cySelector('entities.bulk.statusOption', { slug, value }),
172
+ bulkStatusCancel: cySelector('entities.bulk.statusCancel', { slug }),
173
+ bulkStatusConfirm: cySelector('entities.bulk.statusConfirm', { slug }),
174
+
175
+ // Bulk delete dialog
176
+ bulkDeleteDialog: cySelector('entities.bulk.deleteDialog', { slug }),
177
+ bulkDeleteCancel: cySelector('entities.bulk.deleteCancel', { slug }),
178
+ bulkDeleteConfirm: cySelector('entities.bulk.deleteConfirm', { slug }),
179
+
180
+ // Generic confirm dialog
181
+ confirmDialog: cySelector('entities.confirm.dialog', { slug }),
182
+ confirmCancel: cySelector('entities.confirm.cancel', { slug }),
183
+ confirmAction: cySelector('entities.confirm.action', { slug }),
184
+
185
+ // Parent delete confirmation (EntityDetailWrapper - generic, no slug)
186
+ parentDeleteConfirm: '[data-cy="confirm-delete"]',
187
+ parentDeleteCancel: '[data-cy="cancel-delete"]',
188
+
189
+ // Row action selectors (generic patterns for checking existence)
190
+ rowActionEditGeneric: `[data-cy^="${slug}-action-edit-"]`,
191
+ rowActionDeleteGeneric: `[data-cy^="${slug}-action-delete-"]`,
192
+
193
+ // Detail view
194
+ detail: cySelector('entities.detail.container', { slug })
195
+ }
196
+ }
197
+
198
+ // ============================================
199
+ // NAVIGATION
200
+ // ============================================
201
+
202
+ /**
203
+ * Navigate to entity list page
204
+ */
205
+ visitList() {
206
+ cy.visit(`/dashboard/${this.slug}`)
207
+ return this
208
+ }
209
+
210
+ /**
211
+ * Navigate to create page
212
+ */
213
+ visitCreate() {
214
+ cy.visit(`/dashboard/${this.slug}/create`)
215
+ return this
216
+ }
217
+
218
+ /**
219
+ * Navigate to edit page for specific entity
220
+ */
221
+ visitEdit(id: string) {
222
+ cy.visit(`/dashboard/${this.slug}/${id}/edit`)
223
+ return this
224
+ }
225
+
226
+ /**
227
+ * Navigate to detail/view page for specific entity
228
+ */
229
+ visitDetail(id: string) {
230
+ cy.visit(`/dashboard/${this.slug}/${id}`)
231
+ return this
232
+ }
233
+
234
+ // ============================================
235
+ // API-AWARE NAVIGATION
236
+ // ============================================
237
+
238
+ /**
239
+ * Navigate to list and wait for API response
240
+ */
241
+ visitListWithApiWait() {
242
+ this.setupApiIntercepts()
243
+ this.visitList()
244
+ this.api.waitForList()
245
+ return this
246
+ }
247
+
248
+ /**
249
+ * Navigate to edit page and wait for form to be visible
250
+ */
251
+ visitEditWithApiWait(id: string) {
252
+ this.setupApiIntercepts()
253
+ this.visitEdit(id)
254
+ this.waitForForm()
255
+ return this
256
+ }
257
+
258
+ /**
259
+ * Navigate to detail page and wait for content
260
+ */
261
+ visitDetailWithApiWait(id: string) {
262
+ this.setupApiIntercepts()
263
+ this.visitDetail(id)
264
+ this.waitForDetail()
265
+ return this
266
+ }
267
+
268
+ // ============================================
269
+ // WAITS
270
+ // ============================================
271
+
272
+ /**
273
+ * Wait for list page to be fully loaded
274
+ */
275
+ waitForList() {
276
+ cy.url().should('include', `/dashboard/${this.slug}`)
277
+ cy.get(this.selectors.tableContainer, { timeout: 15000 }).should('be.visible')
278
+ return this
279
+ }
280
+
281
+ /**
282
+ * Wait for form to be visible
283
+ */
284
+ waitForForm() {
285
+ cy.get(this.selectors.form, { timeout: 15000 }).should('be.visible')
286
+ return this
287
+ }
288
+
289
+ /**
290
+ * Wait for detail page to be loaded
291
+ */
292
+ waitForDetail() {
293
+ cy.url().should('match', new RegExp(`/dashboard/${this.slug}/[a-z0-9-]+$`))
294
+ cy.get(this.selectors.editButton, { timeout: 15000 }).should('be.visible')
295
+ return this
296
+ }
297
+
298
+ // ============================================
299
+ // TABLE ACTIONS
300
+ // ============================================
301
+
302
+ /**
303
+ * Click the Add/Create button
304
+ */
305
+ clickAdd() {
306
+ cy.get(this.selectors.addButton).click()
307
+ return this
308
+ }
309
+
310
+ /**
311
+ * Type in the search input
312
+ */
313
+ search(term: string) {
314
+ cy.get(this.selectors.search).clear().type(term)
315
+ return this
316
+ }
317
+
318
+ /**
319
+ * Clear the search input
320
+ */
321
+ clearSearch() {
322
+ cy.get(this.selectors.searchClear).click()
323
+ return this
324
+ }
325
+
326
+ /**
327
+ * Click a specific row by ID
328
+ */
329
+ clickRow(id: string) {
330
+ cy.get(this.selectors.row(id)).click()
331
+ return this
332
+ }
333
+
334
+ /**
335
+ * Find and click a row containing specific text
336
+ */
337
+ clickRowByText(text: string) {
338
+ cy.contains(this.selectors.rowGeneric, text).click()
339
+ return this
340
+ }
341
+
342
+ /**
343
+ * Select a row checkbox
344
+ */
345
+ selectRow(id: string) {
346
+ cy.get(this.selectors.rowSelect(id)).click()
347
+ return this
348
+ }
349
+
350
+ /**
351
+ * Open the row menu (three dots)
352
+ */
353
+ openRowMenu(id: string) {
354
+ cy.get(this.selectors.rowMenu(id)).click()
355
+ return this
356
+ }
357
+
358
+ /**
359
+ * Click an action in the row menu
360
+ */
361
+ clickRowAction(action: string, id: string) {
362
+ cy.get(this.selectors.rowAction(action, id)).click()
363
+ return this
364
+ }
365
+
366
+ // ============================================
367
+ // FILTERS
368
+ // ============================================
369
+
370
+ /**
371
+ * Open a filter dropdown
372
+ */
373
+ openFilter(field: string) {
374
+ cy.get(this.selectors.filterTrigger(field)).click()
375
+ return this
376
+ }
377
+
378
+ /**
379
+ * Select a filter option
380
+ */
381
+ selectFilterOption(field: string, value: string) {
382
+ cy.get(this.selectors.filterOption(field, value)).click()
383
+ return this
384
+ }
385
+
386
+ /**
387
+ * Open filter and select option (convenience method)
388
+ */
389
+ selectFilter(field: string, value: string) {
390
+ this.openFilter(field)
391
+ this.selectFilterOption(field, value)
392
+ return this
393
+ }
394
+
395
+ /**
396
+ * Remove a filter badge
397
+ */
398
+ removeFilterBadge(field: string, value: string) {
399
+ cy.get(this.selectors.filterRemoveBadge(field, value)).click()
400
+ return this
401
+ }
402
+
403
+ /**
404
+ * Clear all values for a filter
405
+ */
406
+ clearFilter(field: string) {
407
+ cy.get(this.selectors.filterClearAll(field)).click()
408
+ return this
409
+ }
410
+
411
+ // ============================================
412
+ // PAGINATION
413
+ // ============================================
414
+
415
+ /**
416
+ * Go to next page
417
+ */
418
+ nextPage() {
419
+ cy.get(this.selectors.pageNext).click()
420
+ return this
421
+ }
422
+
423
+ /**
424
+ * Go to previous page
425
+ */
426
+ prevPage() {
427
+ cy.get(this.selectors.pagePrev).click()
428
+ return this
429
+ }
430
+
431
+ /**
432
+ * Go to first page
433
+ */
434
+ firstPage() {
435
+ cy.get(this.selectors.pageFirst).click()
436
+ return this
437
+ }
438
+
439
+ /**
440
+ * Go to last page
441
+ */
442
+ lastPage() {
443
+ cy.get(this.selectors.pageLast).click()
444
+ return this
445
+ }
446
+
447
+ /**
448
+ * Change page size
449
+ */
450
+ setPageSize(size: string) {
451
+ cy.get(this.selectors.pageSize).click()
452
+ cy.get(this.selectors.pageSizeOption(size)).click()
453
+ return this
454
+ }
455
+
456
+ // ============================================
457
+ // FORM ACTIONS
458
+ // ============================================
459
+
460
+ /**
461
+ * Fill a text input field
462
+ */
463
+ fillTextField(name: string, value: string) {
464
+ cy.get(this.selectors.field(name)).find('input').clear().type(value)
465
+ return this
466
+ }
467
+
468
+ /**
469
+ * Fill a textarea field
470
+ */
471
+ fillTextarea(name: string, value: string) {
472
+ cy.get(this.selectors.field(name)).find('textarea').clear().type(value)
473
+ return this
474
+ }
475
+
476
+ /**
477
+ * Select an option in a combobox/select field
478
+ */
479
+ selectOption(name: string, value: string) {
480
+ cy.get(this.selectors.field(name)).find('[role="combobox"]').click()
481
+ cy.get(`[data-cy="${this.slug}-field-${name}-option-${value}"]`).click()
482
+ return this
483
+ }
484
+
485
+ /**
486
+ * Submit the form
487
+ */
488
+ submitForm() {
489
+ cy.get(this.selectors.submitButton).click()
490
+ return this
491
+ }
492
+
493
+
494
+ // ============================================
495
+ // HEADER/DETAIL ACTIONS
496
+ // ============================================
497
+
498
+ /**
499
+ * Click back button
500
+ */
501
+ clickBack() {
502
+ cy.get(this.selectors.backButton).click()
503
+ return this
504
+ }
505
+
506
+ /**
507
+ * Click edit button
508
+ */
509
+ clickEdit() {
510
+ cy.get(this.selectors.editButton).click()
511
+ return this
512
+ }
513
+
514
+ /**
515
+ * Click delete button
516
+ */
517
+ clickDelete() {
518
+ cy.get(this.selectors.deleteButton).click()
519
+ return this
520
+ }
521
+
522
+ /**
523
+ * Confirm delete in dialog
524
+ */
525
+ confirmDelete() {
526
+ cy.get(this.selectors.deleteConfirm).click()
527
+ return this
528
+ }
529
+
530
+ /**
531
+ * Cancel delete in dialog
532
+ */
533
+ cancelDelete() {
534
+ cy.get(this.selectors.deleteCancel).click()
535
+ return this
536
+ }
537
+
538
+ // ============================================
539
+ // BULK ACTIONS
540
+ // ============================================
541
+
542
+ /**
543
+ * Select all items using table header checkbox
544
+ */
545
+ selectAll() {
546
+ cy.get(this.selectors.selectAll).click()
547
+ return this
548
+ }
549
+
550
+ /**
551
+ * Click bulk delete button
552
+ */
553
+ bulkDelete() {
554
+ cy.get(this.selectors.bulkDelete).click()
555
+ return this
556
+ }
557
+
558
+ /**
559
+ * Confirm bulk delete
560
+ */
561
+ confirmBulkDelete() {
562
+ cy.get(this.selectors.bulkDeleteConfirm).click()
563
+ return this
564
+ }
565
+
566
+ /**
567
+ * Cancel bulk delete
568
+ */
569
+ cancelBulkDelete() {
570
+ cy.get(this.selectors.bulkDeleteCancel).click()
571
+ return this
572
+ }
573
+
574
+ /**
575
+ * Click bulk status button
576
+ */
577
+ bulkChangeStatus() {
578
+ cy.get(this.selectors.bulkStatus).click()
579
+ return this
580
+ }
581
+
582
+ /**
583
+ * Select status in bulk status dialog
584
+ */
585
+ selectBulkStatus(value: string) {
586
+ cy.get(this.selectors.bulkStatusSelect).click()
587
+ cy.get(this.selectors.bulkStatusOption(value)).click()
588
+ return this
589
+ }
590
+
591
+ /**
592
+ * Confirm bulk status change
593
+ */
594
+ confirmBulkStatus() {
595
+ cy.get(this.selectors.bulkStatusConfirm).click()
596
+ return this
597
+ }
598
+
599
+ /**
600
+ * Clear selection
601
+ */
602
+ clearSelection() {
603
+ cy.get(this.selectors.bulkClear).click()
604
+ return this
605
+ }
606
+
607
+ // ============================================
608
+ // ASSERTIONS
609
+ // ============================================
610
+
611
+ /**
612
+ * Assert text is visible in the list
613
+ */
614
+ assertInList(text: string) {
615
+ cy.contains(text).should('be.visible')
616
+ return this
617
+ }
618
+
619
+ /**
620
+ * Assert text is not in the list
621
+ */
622
+ assertNotInList(text: string) {
623
+ cy.contains(text).should('not.exist')
624
+ return this
625
+ }
626
+
627
+ /**
628
+ * Assert table is visible
629
+ */
630
+ assertTableVisible() {
631
+ cy.get(this.selectors.table).should('be.visible')
632
+ return this
633
+ }
634
+
635
+ /**
636
+ * Assert form is visible
637
+ */
638
+ assertFormVisible() {
639
+ cy.get(this.selectors.form).should('be.visible')
640
+ return this
641
+ }
642
+
643
+ /**
644
+ * Assert page title contains text
645
+ */
646
+ assertPageTitle(expected: string) {
647
+ cy.get(this.selectors.title).should('contain.text', expected)
648
+ return this
649
+ }
650
+
651
+ /**
652
+ * Assert row exists
653
+ */
654
+ assertRowExists(id: string) {
655
+ cy.get(this.selectors.row(id)).should('exist')
656
+ return this
657
+ }
658
+
659
+ /**
660
+ * Assert row does not exist
661
+ */
662
+ assertRowNotExists(id: string) {
663
+ cy.get(this.selectors.row(id)).should('not.exist')
664
+ return this
665
+ }
666
+
667
+ /**
668
+ * Assert selection count
669
+ */
670
+ assertSelectionCount(count: number) {
671
+ cy.get(this.selectors.selectionCount).should('contain.text', count.toString())
672
+ return this
673
+ }
674
+
675
+ /**
676
+ * Assert bulk bar is visible
677
+ */
678
+ assertBulkBarVisible() {
679
+ cy.get(this.selectors.bulkBar).should('be.visible')
680
+ return this
681
+ }
682
+
683
+ /**
684
+ * Assert bulk bar is hidden
685
+ */
686
+ assertBulkBarHidden() {
687
+ cy.get(this.selectors.bulkBar).should('not.be.visible')
688
+ return this
689
+ }
690
+ }
691
+
692
+ export default DashboardEntityPOM
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Core POM exports
3
+ *
4
+ * Base classes for building Page Object Models:
5
+ * - BasePOM: Utility methods for all POMs
6
+ * - DashboardEntityPOM: Base for entity CRUD POMs
7
+ * - BlockEditorBasePOM: Base for page/post builder POMs
8
+ * - AuthPOM: Authentication pages POM
9
+ */
10
+
11
+ export { BasePOM } from './BasePOM'
12
+ export { DashboardEntityPOM, type EntityConfig } from './DashboardEntityPOM'
13
+ export { BlockEditorBasePOM } from './BlockEditorBasePOM'
14
+ export { AuthPOM, type SignupData } from './AuthPOM'