@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,263 @@
1
+ /**
2
+ * PageBuilderPOM - Page Object Model for Page Builder (create/edit pages with blocks)
3
+ *
4
+ * Extends BlockEditorBasePOM with page-specific functionality:
5
+ * - Page settings (locale, status)
6
+ * - SEO settings
7
+ * - Page-specific workflows
8
+ *
9
+ * For list operations (search, filter, delete), use PagesPOM from entities/.
10
+ *
11
+ * @example
12
+ * // Create a new page
13
+ * PageBuilderPOM.create()
14
+ * .visitCreate()
15
+ * .waitForEditor()
16
+ * .setTitle('About Us')
17
+ * .setSlug('about-us')
18
+ * .addBlock('hero-section')
19
+ * .addBlock('text-content')
20
+ * .setStatus('published')
21
+ * .save()
22
+ *
23
+ * // Edit existing page
24
+ * PageBuilderPOM.create()
25
+ * .visitEdit(pageId)
26
+ * .waitForEditor()
27
+ * .setTitle('New Title')
28
+ * .saveWithWait()
29
+ */
30
+
31
+ import { BlockEditorBasePOM } from '../core/BlockEditorBasePOM'
32
+ import { cySelector } from '../selectors'
33
+ import entitiesConfig from '../../fixtures/entities.json'
34
+
35
+ export interface PageFormData {
36
+ title: string
37
+ slug: string
38
+ locale?: 'en' | 'es'
39
+ status?: 'draft' | 'published' | 'scheduled' | 'archived'
40
+ metaTitle?: string
41
+ metaDescription?: string
42
+ metaKeywords?: string
43
+ }
44
+
45
+ export interface BlockData {
46
+ slug: string
47
+ props?: Record<string, unknown>
48
+ }
49
+
50
+ export class PageBuilderPOM extends BlockEditorBasePOM {
51
+ protected entitySlug = entitiesConfig.entities.pages.slug
52
+
53
+ // ============================================
54
+ // FACTORY METHOD
55
+ // ============================================
56
+
57
+ static create(): PageBuilderPOM {
58
+ return new PageBuilderPOM()
59
+ }
60
+
61
+ // ============================================
62
+ // PAGE-SPECIFIC SELECTORS
63
+ // ============================================
64
+
65
+ get pageSelectors() {
66
+ return {
67
+ // Locale (pages only) - uses centralized selectors
68
+ localeSelect: cySelector('blockEditor.localeField.select'),
69
+ localeOption: (locale: string) => cySelector('blockEditor.localeField.option', { locale })
70
+ }
71
+ }
72
+
73
+ // ============================================
74
+ // PAGE-SPECIFIC METHODS
75
+ // ============================================
76
+
77
+ /**
78
+ * Set page locale
79
+ */
80
+ setLocale(locale: 'en' | 'es') {
81
+ cy.get(this.pageSelectors.localeSelect).click()
82
+ cy.get(this.pageSelectors.localeOption(locale)).click()
83
+ return this
84
+ }
85
+
86
+ /**
87
+ * Publish the page (set status to published and save)
88
+ */
89
+ publishPage() {
90
+ this.setStatus('published')
91
+ this.save()
92
+ return this
93
+ }
94
+
95
+ /**
96
+ * Publish with API wait
97
+ */
98
+ publishPageWithWait() {
99
+ this.setupApiIntercepts()
100
+ this.setStatus('published')
101
+ this.save()
102
+ this.api.waitForUpdate()
103
+ return this
104
+ }
105
+
106
+ /**
107
+ * Unpublish the page (set status to draft)
108
+ */
109
+ unpublishPage() {
110
+ this.setStatus('draft')
111
+ this.save()
112
+ return this
113
+ }
114
+
115
+ /**
116
+ * Archive the page
117
+ */
118
+ archivePage() {
119
+ this.setStatus('archived')
120
+ this.save()
121
+ return this
122
+ }
123
+
124
+ // ============================================
125
+ // WORKFLOW METHODS
126
+ // ============================================
127
+
128
+ /**
129
+ * Create a new page with specified data and blocks
130
+ */
131
+ createPage(data: PageFormData, blocks: BlockData[] = []) {
132
+ this.visitCreate()
133
+ this.waitForEditor()
134
+
135
+ // Set page settings
136
+ this.setTitle(data.title)
137
+ this.setSlug(data.slug)
138
+
139
+ if (data.locale) {
140
+ this.setLocale(data.locale)
141
+ }
142
+
143
+ // Add blocks
144
+ blocks.forEach((block) => {
145
+ this.addBlock(block.slug)
146
+ })
147
+
148
+ // Set status if provided
149
+ if (data.status) {
150
+ this.setStatus(data.status)
151
+ }
152
+
153
+ // Set SEO if provided
154
+ if (data.metaTitle || data.metaDescription || data.metaKeywords) {
155
+ this.openSeoSettings()
156
+ if (data.metaTitle) this.setMetaTitle(data.metaTitle)
157
+ if (data.metaDescription) this.setMetaDescription(data.metaDescription)
158
+ if (data.metaKeywords) this.setMetaKeywords(data.metaKeywords)
159
+ }
160
+
161
+ this.save()
162
+ return this
163
+ }
164
+
165
+ /**
166
+ * Create page with API waits (deterministic)
167
+ */
168
+ createPageWithApiWait(data: PageFormData, blocks: BlockData[] = []) {
169
+ this.setupApiIntercepts()
170
+ this.visitCreate()
171
+ this.waitForEditor()
172
+
173
+ this.setTitle(data.title)
174
+ this.setSlug(data.slug)
175
+
176
+ if (data.locale) this.setLocale(data.locale)
177
+
178
+ blocks.forEach((block) => this.addBlock(block.slug))
179
+
180
+ if (data.status) this.setStatus(data.status)
181
+
182
+ if (data.metaTitle || data.metaDescription || data.metaKeywords) {
183
+ this.openSeoSettings()
184
+ if (data.metaTitle) this.setMetaTitle(data.metaTitle)
185
+ if (data.metaDescription) this.setMetaDescription(data.metaDescription)
186
+ if (data.metaKeywords) this.setMetaKeywords(data.metaKeywords)
187
+ }
188
+
189
+ this.save()
190
+ this.api.waitForCreate()
191
+ return this
192
+ }
193
+
194
+ /**
195
+ * Edit an existing page
196
+ */
197
+ editPage(id: string, updates: Partial<PageFormData>) {
198
+ this.visitEdit(id)
199
+ this.waitForEditor()
200
+
201
+ if (updates.title) this.setTitle(updates.title)
202
+ if (updates.slug) this.setSlug(updates.slug)
203
+ if (updates.locale) this.setLocale(updates.locale)
204
+ if (updates.status) this.setStatus(updates.status)
205
+
206
+ if (updates.metaTitle || updates.metaDescription || updates.metaKeywords) {
207
+ this.openSeoSettings()
208
+ if (updates.metaTitle) this.setMetaTitle(updates.metaTitle)
209
+ if (updates.metaDescription) this.setMetaDescription(updates.metaDescription)
210
+ if (updates.metaKeywords) this.setMetaKeywords(updates.metaKeywords)
211
+ }
212
+
213
+ this.save()
214
+ return this
215
+ }
216
+
217
+ /**
218
+ * Edit page with API waits
219
+ */
220
+ editPageWithApiWait(id: string, updates: Partial<PageFormData>) {
221
+ this.setupApiIntercepts()
222
+ this.editPage(id, updates)
223
+ this.api.waitForUpdate()
224
+ return this
225
+ }
226
+
227
+ // ============================================
228
+ // PAGE-SPECIFIC ASSERTIONS
229
+ // ============================================
230
+
231
+ /**
232
+ * Assert page status badge shows specific status
233
+ */
234
+ assertPageStatus(status: 'draft' | 'published' | 'scheduled' | 'archived') {
235
+ return this.assertStatus(status)
236
+ }
237
+
238
+ /**
239
+ * Assert locale is selected
240
+ */
241
+ assertLocale(locale: 'en' | 'es') {
242
+ cy.get(this.pageSelectors.localeSelect).should('contain.text', locale)
243
+ return this
244
+ }
245
+
246
+ /**
247
+ * Assert save was successful
248
+ */
249
+ assertSaveSuccess() {
250
+ cy.contains('saved', { matchCase: false }).should('be.visible')
251
+ return this
252
+ }
253
+
254
+ /**
255
+ * Assert publish was successful
256
+ */
257
+ assertPublishSuccess() {
258
+ this.assertStatus('published')
259
+ return this
260
+ }
261
+ }
262
+
263
+ export default PageBuilderPOM
@@ -0,0 +1,313 @@
1
+ /**
2
+ * PostEditorPOM - Page Object Model for Post Editor (create/edit posts with blocks)
3
+ *
4
+ * Extends BlockEditorBasePOM with post-specific functionality:
5
+ * - Excerpt
6
+ * - Featured image
7
+ * - Categories
8
+ * - Post-specific workflows
9
+ *
10
+ * For list operations (search, filter, delete), use PostsPOM from entities/.
11
+ *
12
+ * @example
13
+ * // Create a new post
14
+ * PostEditorPOM.create()
15
+ * .visitCreate()
16
+ * .waitForEditor()
17
+ * .setTitle('My Blog Post')
18
+ * .setSlug('my-blog-post')
19
+ * .setExcerpt('A brief summary...')
20
+ * .addBlock('text-content')
21
+ * .setStatus('published')
22
+ * .save()
23
+ *
24
+ * // Edit existing post
25
+ * PostEditorPOM.create()
26
+ * .visitEdit(postId)
27
+ * .waitForEditor()
28
+ * .setTitle('Updated Title')
29
+ * .saveWithWait()
30
+ */
31
+
32
+ import { BlockEditorBasePOM } from '../core/BlockEditorBasePOM'
33
+ import { cySelector } from '../selectors'
34
+ import entitiesConfig from '../../fixtures/entities.json'
35
+
36
+ export interface PostFormData {
37
+ title: string
38
+ slug: string
39
+ excerpt?: string
40
+ featuredImage?: string
41
+ categoryIds?: string[]
42
+ status?: 'draft' | 'published' | 'archived'
43
+ locale?: 'en' | 'es'
44
+ }
45
+
46
+ export interface BlockData {
47
+ slug: string
48
+ props?: Record<string, unknown>
49
+ }
50
+
51
+ export class PostEditorPOM extends BlockEditorBasePOM {
52
+ protected entitySlug = entitiesConfig.entities.posts.slug
53
+
54
+ // ============================================
55
+ // FACTORY METHOD
56
+ // ============================================
57
+
58
+ static create(): PostEditorPOM {
59
+ return new PostEditorPOM()
60
+ }
61
+
62
+ // ============================================
63
+ // POST-SPECIFIC SELECTORS
64
+ // ============================================
65
+
66
+ get postSelectors() {
67
+ return {
68
+ // Post-specific fields (using centralized selectors)
69
+ excerptInput: cySelector('blockEditor.postFields.excerpt'),
70
+ featuredImage: cySelector('blockEditor.postFields.featuredImage'),
71
+ featuredImageUpload: cySelector('blockEditor.postFields.featuredImageUpload'),
72
+ categoriesSelect: cySelector('blockEditor.postFields.categories'),
73
+ categoryOption: (id: string) => cySelector('blockEditor.postFields.categoryOption', { id }),
74
+ categoryBadge: (id: string) => cySelector('blockEditor.postFields.categoryBadge', { id }),
75
+ categoryRemove: (id: string) => cySelector('blockEditor.postFields.categoryRemove', { id }),
76
+
77
+ // Locale (posts may also have locale)
78
+ localeSelect: cySelector('blockEditor.localeField.select'),
79
+ localeOption: (locale: string) => cySelector('blockEditor.localeField.option', { locale })
80
+ }
81
+ }
82
+
83
+ // ============================================
84
+ // POST-SPECIFIC METHODS
85
+ // ============================================
86
+
87
+ /**
88
+ * Set post excerpt
89
+ */
90
+ setExcerpt(excerpt: string) {
91
+ cy.get(this.postSelectors.excerptInput).clear().type(excerpt)
92
+ return this
93
+ }
94
+
95
+ /**
96
+ * Set featured image URL
97
+ */
98
+ setFeaturedImage(url: string) {
99
+ cy.get(this.postSelectors.featuredImage).clear().type(url)
100
+ return this
101
+ }
102
+
103
+ /**
104
+ * Select categories for the post
105
+ */
106
+ selectCategories(categoryIds: string[]) {
107
+ cy.get(this.postSelectors.categoriesSelect).click()
108
+ categoryIds.forEach((id) => {
109
+ cy.get(this.postSelectors.categoryOption(id)).click()
110
+ })
111
+ // Close dropdown by clicking outside
112
+ cy.get(this.editorSelectors.container).click()
113
+ return this
114
+ }
115
+
116
+ /**
117
+ * Remove a category from the post
118
+ */
119
+ removeCategory(categoryId: string) {
120
+ cy.get(this.postSelectors.categoryRemove(categoryId)).click()
121
+ return this
122
+ }
123
+
124
+ /**
125
+ * Set post locale
126
+ */
127
+ setLocale(locale: 'en' | 'es') {
128
+ cy.get(this.postSelectors.localeSelect).click()
129
+ cy.get(this.postSelectors.localeOption(locale)).click()
130
+ return this
131
+ }
132
+
133
+ /**
134
+ * Publish the post (set status to published and save)
135
+ */
136
+ publishPost() {
137
+ this.setStatus('published')
138
+ this.save()
139
+ return this
140
+ }
141
+
142
+ /**
143
+ * Publish with API wait
144
+ */
145
+ publishPostWithWait() {
146
+ this.setupApiIntercepts()
147
+ this.setStatus('published')
148
+ this.save()
149
+ this.api.waitForUpdate()
150
+ return this
151
+ }
152
+
153
+ /**
154
+ * Unpublish the post (set status to draft)
155
+ */
156
+ unpublishPost() {
157
+ this.setStatus('draft')
158
+ this.save()
159
+ return this
160
+ }
161
+
162
+ // ============================================
163
+ // WORKFLOW METHODS
164
+ // ============================================
165
+
166
+ /**
167
+ * Create a new post with specified data and blocks
168
+ */
169
+ createPost(data: PostFormData, blocks: BlockData[] = []) {
170
+ this.visitCreate()
171
+ this.waitForEditor()
172
+
173
+ // Set post settings
174
+ this.setTitle(data.title)
175
+ this.setSlug(data.slug)
176
+
177
+ if (data.excerpt) {
178
+ this.setExcerpt(data.excerpt)
179
+ }
180
+
181
+ if (data.featuredImage) {
182
+ this.setFeaturedImage(data.featuredImage)
183
+ }
184
+
185
+ if (data.categoryIds && data.categoryIds.length > 0) {
186
+ this.selectCategories(data.categoryIds)
187
+ }
188
+
189
+ if (data.locale) {
190
+ this.setLocale(data.locale)
191
+ }
192
+
193
+ // Add blocks
194
+ blocks.forEach((block) => {
195
+ this.addBlock(block.slug)
196
+ })
197
+
198
+ // Set status if provided
199
+ if (data.status) {
200
+ this.setStatus(data.status)
201
+ }
202
+
203
+ this.save()
204
+ return this
205
+ }
206
+
207
+ /**
208
+ * Create post with API waits (deterministic)
209
+ */
210
+ createPostWithApiWait(data: PostFormData, blocks: BlockData[] = []) {
211
+ this.setupApiIntercepts()
212
+ this.visitCreate()
213
+ this.waitForEditor()
214
+
215
+ this.setTitle(data.title)
216
+ this.setSlug(data.slug)
217
+
218
+ if (data.excerpt) this.setExcerpt(data.excerpt)
219
+ if (data.featuredImage) this.setFeaturedImage(data.featuredImage)
220
+ if (data.categoryIds && data.categoryIds.length > 0) this.selectCategories(data.categoryIds)
221
+ if (data.locale) this.setLocale(data.locale)
222
+
223
+ blocks.forEach((block) => this.addBlock(block.slug))
224
+
225
+ if (data.status) this.setStatus(data.status)
226
+
227
+ this.save()
228
+ this.api.waitForCreate()
229
+ return this
230
+ }
231
+
232
+ /**
233
+ * Edit an existing post
234
+ */
235
+ editPost(id: string, updates: Partial<PostFormData>) {
236
+ this.visitEdit(id)
237
+ this.waitForEditor()
238
+
239
+ if (updates.title) this.setTitle(updates.title)
240
+ if (updates.slug) this.setSlug(updates.slug)
241
+ if (updates.excerpt) this.setExcerpt(updates.excerpt)
242
+ if (updates.featuredImage) this.setFeaturedImage(updates.featuredImage)
243
+ if (updates.categoryIds) this.selectCategories(updates.categoryIds)
244
+ if (updates.locale) this.setLocale(updates.locale)
245
+ if (updates.status) this.setStatus(updates.status)
246
+
247
+ this.save()
248
+ return this
249
+ }
250
+
251
+ /**
252
+ * Edit post with API waits
253
+ */
254
+ editPostWithApiWait(id: string, updates: Partial<PostFormData>) {
255
+ this.setupApiIntercepts()
256
+ this.editPost(id, updates)
257
+ this.api.waitForUpdate()
258
+ return this
259
+ }
260
+
261
+ // ============================================
262
+ // POST-SPECIFIC ASSERTIONS
263
+ // ============================================
264
+
265
+ /**
266
+ * Assert post status badge shows specific status
267
+ */
268
+ assertPostStatus(status: 'draft' | 'published' | 'archived') {
269
+ return this.assertStatus(status)
270
+ }
271
+
272
+ /**
273
+ * Assert excerpt value
274
+ */
275
+ assertExcerpt(excerpt: string) {
276
+ cy.get(this.postSelectors.excerptInput).should('have.value', excerpt)
277
+ return this
278
+ }
279
+
280
+ /**
281
+ * Assert featured image URL
282
+ */
283
+ assertFeaturedImage(url: string) {
284
+ cy.get(this.postSelectors.featuredImage).should('have.value', url)
285
+ return this
286
+ }
287
+
288
+ /**
289
+ * Assert category is selected
290
+ */
291
+ assertCategorySelected(categoryId: string) {
292
+ cy.get(this.postSelectors.categoryBadge(categoryId)).should('be.visible')
293
+ return this
294
+ }
295
+
296
+ /**
297
+ * Assert save was successful
298
+ */
299
+ assertSaveSuccess() {
300
+ cy.contains('saved', { matchCase: false }).should('be.visible')
301
+ return this
302
+ }
303
+
304
+ /**
305
+ * Assert publish was successful
306
+ */
307
+ assertPublishSuccess() {
308
+ this.assertStatus('published')
309
+ return this
310
+ }
311
+ }
312
+
313
+ export default PostEditorPOM