@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,396 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Blocks Scope API - Tests
5
+ *
6
+ * Test suite for blocks scope filtering functionality.
7
+ * Validates that GET /api/v1/blocks correctly filters blocks by scope parameter.
8
+ *
9
+ * Scope types:
10
+ * - 'pages': Blocks available in page builder
11
+ * - 'posts': Blocks available in post editor
12
+ * - No scope: Returns all blocks
13
+ *
14
+ * Tags: @api, @feat-page-builder, @scope
15
+ */
16
+
17
+ import * as allure from 'allure-cypress'
18
+
19
+ describe('Blocks Scope API - Filtering Tests', {
20
+ tags: ['@api', '@feat-page-builder', '@scope']
21
+ }, () => {
22
+ // Test constants
23
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
24
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
25
+ const API_BLOCKS = `${BASE_URL}/api/v1/blocks`
26
+
27
+ // Helper to make authenticated API key requests
28
+ const apiRequest = (method: string, url: string, body?: object) => {
29
+ return cy.request({
30
+ method,
31
+ url,
32
+ headers: {
33
+ 'x-api-key': SUPERADMIN_API_KEY,
34
+ 'Content-Type': 'application/json'
35
+ },
36
+ body,
37
+ failOnStatusCode: false
38
+ })
39
+ }
40
+
41
+ beforeEach(() => {
42
+ allure.epic('API')
43
+ allure.feature('Blocks')
44
+ })
45
+
46
+ // ============================================================
47
+ // GET /api/v1/blocks - List All Blocks (No Scope Filter)
48
+ // ============================================================
49
+ describe('GET /api/v1/blocks - List All Blocks', () => {
50
+ it('BLOCKS_API_001: Should list all blocks without scope filter', { tags: '@smoke' }, () => {
51
+ allure.story('Scope Filtering')
52
+ allure.severity('critical')
53
+
54
+ apiRequest('GET', API_BLOCKS).then((response) => {
55
+ expect(response.status).to.eq(200)
56
+ expect(response.body.success).to.be.true
57
+ expect(response.body.data).to.be.an('array')
58
+ expect(response.body.data.length).to.be.greaterThan(0)
59
+
60
+ cy.log(`Found ${response.body.data.length} total blocks`)
61
+ })
62
+ })
63
+
64
+ it('BLOCKS_API_002: Should return blocks with expected structure', () => {
65
+ apiRequest('GET', API_BLOCKS).then((response) => {
66
+ expect(response.status).to.eq(200)
67
+
68
+ if (response.body.data.length > 0) {
69
+ const block = response.body.data[0]
70
+
71
+ // Verify block structure
72
+ expect(block).to.have.property('slug')
73
+ expect(block).to.have.property('name')
74
+ expect(block).to.have.property('description')
75
+ expect(block).to.have.property('category')
76
+ expect(block).to.have.property('fieldDefinitions')
77
+ expect(block).to.have.property('scope')
78
+
79
+ cy.log(`Block structure verified: ${block.name}`)
80
+ }
81
+ })
82
+ })
83
+ })
84
+
85
+ // ============================================================
86
+ // GET /api/v1/blocks?scope=pages - Filter by Pages Scope
87
+ // ============================================================
88
+ describe('GET /api/v1/blocks?scope=pages - Filter by Pages Scope', () => {
89
+ it('BLOCKS_API_010: Should filter blocks by scope=pages', { tags: '@smoke' }, () => {
90
+ allure.story('Scope Filtering')
91
+ allure.severity('critical')
92
+
93
+ apiRequest('GET', `${API_BLOCKS}?scope=pages`).then((response) => {
94
+ expect(response.status).to.eq(200)
95
+ expect(response.body.success).to.be.true
96
+ expect(response.body.data).to.be.an('array')
97
+
98
+ // All returned blocks should include 'pages' in their scope
99
+ response.body.data.forEach((block: any) => {
100
+ expect(block.scope).to.exist
101
+ expect(block.scope).to.be.an('array')
102
+ expect(block.scope).to.include('pages')
103
+ })
104
+
105
+ cy.log(`Found ${response.body.data.length} blocks with scope 'pages'`)
106
+ })
107
+ })
108
+
109
+ it('BLOCKS_API_011: Should include expected blocks for pages', () => {
110
+ apiRequest('GET', `${API_BLOCKS}?scope=pages`).then((response) => {
111
+ expect(response.status).to.eq(200)
112
+
113
+ const blockSlugs = response.body.data.map((b: any) => b.slug)
114
+
115
+ // Expected blocks for pages (from plan.md)
116
+ const expectedBlocks = [
117
+ 'cta-section',
118
+ 'features-grid',
119
+ 'testimonials',
120
+ 'text-content',
121
+ 'benefits'
122
+ ]
123
+
124
+ expectedBlocks.forEach((expectedSlug) => {
125
+ expect(blockSlugs).to.include(expectedSlug)
126
+ cy.log(`Verified block '${expectedSlug}' is available for pages`)
127
+ })
128
+ })
129
+ })
130
+
131
+ it('BLOCKS_API_012: Should not include posts-only blocks', () => {
132
+ apiRequest('GET', `${API_BLOCKS}?scope=pages`).then((response) => {
133
+ expect(response.status).to.eq(200)
134
+
135
+ // Verify no block has scope=['posts'] only (without 'pages')
136
+ response.body.data.forEach((block: any) => {
137
+ if (block.scope && block.scope.length === 1 && block.scope[0] === 'posts') {
138
+ throw new Error(`Block '${block.slug}' should not appear in pages scope`)
139
+ }
140
+ })
141
+
142
+ cy.log('Verified posts-only blocks are excluded')
143
+ })
144
+ })
145
+ })
146
+
147
+ // ============================================================
148
+ // GET /api/v1/blocks?scope=posts - Filter by Posts Scope
149
+ // ============================================================
150
+ describe('GET /api/v1/blocks?scope=posts - Filter by Posts Scope', () => {
151
+ it('BLOCKS_API_020: Should filter blocks by scope=posts', { tags: '@smoke' }, () => {
152
+ allure.story('Scope Filtering')
153
+ allure.severity('critical')
154
+
155
+ apiRequest('GET', `${API_BLOCKS}?scope=posts`).then((response) => {
156
+ expect(response.status).to.eq(200)
157
+ expect(response.body.success).to.be.true
158
+ expect(response.body.data).to.be.an('array')
159
+
160
+ // All returned blocks should include 'posts' in their scope
161
+ response.body.data.forEach((block: any) => {
162
+ expect(block.scope).to.exist
163
+ expect(block.scope).to.be.an('array')
164
+ expect(block.scope).to.include('posts')
165
+ })
166
+
167
+ cy.log(`Found ${response.body.data.length} blocks with scope 'posts'`)
168
+ })
169
+ })
170
+
171
+ it('BLOCKS_API_021: Should include hero block for posts', () => {
172
+ apiRequest('GET', `${API_BLOCKS}?scope=posts`).then((response) => {
173
+ expect(response.status).to.eq(200)
174
+
175
+ const blockSlugs = response.body.data.map((b: any) => b.slug)
176
+
177
+ // Hero block should be available for posts (from plan.md)
178
+ expect(blockSlugs).to.include('hero')
179
+
180
+ const heroBlock = response.body.data.find((b: any) => b.slug === 'hero')
181
+ expect(heroBlock).to.exist
182
+ expect(heroBlock.scope).to.include('posts')
183
+
184
+ cy.log("Verified 'hero' block is available for posts")
185
+ })
186
+ })
187
+
188
+ it('BLOCKS_API_022: Should have fewer blocks than pages scope', () => {
189
+ // Get blocks for pages
190
+ apiRequest('GET', `${API_BLOCKS}?scope=pages`).then((pagesResponse) => {
191
+ const pagesCount = pagesResponse.body.data.length
192
+
193
+ // Get blocks for posts
194
+ apiRequest('GET', `${API_BLOCKS}?scope=posts`).then((postsResponse) => {
195
+ const postsCount = postsResponse.body.data.length
196
+
197
+ // Posts scope should have fewer blocks (only hero according to plan.md)
198
+ expect(postsCount).to.be.lessThan(pagesCount)
199
+
200
+ cy.log(`Pages blocks: ${pagesCount}, Posts blocks: ${postsCount}`)
201
+ })
202
+ })
203
+ })
204
+
205
+ it('BLOCKS_API_023: Should not include pages-only blocks', () => {
206
+ apiRequest('GET', `${API_BLOCKS}?scope=posts`).then((response) => {
207
+ expect(response.status).to.eq(200)
208
+
209
+ // Verify no block has scope=['pages'] only (without 'posts')
210
+ response.body.data.forEach((block: any) => {
211
+ if (block.scope && block.scope.length === 1 && block.scope[0] === 'pages') {
212
+ throw new Error(`Block '${block.slug}' should not appear in posts scope`)
213
+ }
214
+ })
215
+
216
+ cy.log('Verified pages-only blocks are excluded')
217
+ })
218
+ })
219
+ })
220
+
221
+ // ============================================================
222
+ // Edge Cases and Validation
223
+ // ============================================================
224
+ describe('Edge Cases and Validation', () => {
225
+ it('BLOCKS_API_030: Should handle invalid scope parameter gracefully', () => {
226
+ apiRequest('GET', `${API_BLOCKS}?scope=invalid-scope`).then((response) => {
227
+ expect(response.status).to.eq(200)
228
+ expect(response.body.success).to.be.true
229
+ expect(response.body.data).to.be.an('array')
230
+
231
+ // Should return empty array or no blocks matching the invalid scope
232
+ // (depending on implementation)
233
+ cy.log(`Invalid scope returned ${response.body.data.length} blocks`)
234
+ })
235
+ })
236
+
237
+ it('BLOCKS_API_031: Should handle empty scope parameter', () => {
238
+ apiRequest('GET', `${API_BLOCKS}?scope=`).then((response) => {
239
+ expect(response.status).to.eq(200)
240
+ expect(response.body.success).to.be.true
241
+ expect(response.body.data).to.be.an('array')
242
+
243
+ cy.log(`Empty scope parameter returned ${response.body.data.length} blocks`)
244
+ })
245
+ })
246
+
247
+ it('BLOCKS_API_032: Should handle multiple scope parameters', () => {
248
+ // Test if API supports multiple scopes (optional feature)
249
+ apiRequest('GET', `${API_BLOCKS}?scope=pages&scope=posts`).then((response) => {
250
+ expect(response.status).to.eq(200)
251
+ expect(response.body.success).to.be.true
252
+ expect(response.body.data).to.be.an('array')
253
+
254
+ cy.log(`Multiple scope parameters returned ${response.body.data.length} blocks`)
255
+ })
256
+ })
257
+
258
+ it('BLOCKS_API_033: Should verify blocks without scope are not returned', () => {
259
+ // Get all blocks
260
+ apiRequest('GET', API_BLOCKS).then((allResponse) => {
261
+ // Verify all blocks have a scope property
262
+ allResponse.body.data.forEach((block: any) => {
263
+ expect(block).to.have.property('scope')
264
+ // Blocks with undefined or empty scope should not appear in filtered results
265
+ })
266
+
267
+ cy.log('Verified all blocks have scope property')
268
+ })
269
+ })
270
+ })
271
+
272
+ // ============================================================
273
+ // Integration - Scope Consistency
274
+ // ============================================================
275
+ describe('Integration - Scope Consistency', () => {
276
+ it('BLOCKS_API_100: Should maintain scope consistency across requests', () => {
277
+ // Get all blocks
278
+ apiRequest('GET', API_BLOCKS).then((allResponse) => {
279
+ const allBlocks = allResponse.body.data
280
+ const totalBlocks = allBlocks.length
281
+
282
+ // Get pages scope
283
+ apiRequest('GET', `${API_BLOCKS}?scope=pages`).then((pagesResponse) => {
284
+ const pagesBlocks = pagesResponse.body.data
285
+
286
+ // Get posts scope
287
+ apiRequest('GET', `${API_BLOCKS}?scope=posts`).then((postsResponse) => {
288
+ const postsBlocks = postsResponse.body.data
289
+
290
+ // Verify consistency
291
+ cy.log(`Total blocks: ${totalBlocks}`)
292
+ cy.log(`Pages blocks: ${pagesBlocks.length}`)
293
+ cy.log(`Posts blocks: ${postsBlocks.length}`)
294
+
295
+ // Verify all filtered blocks exist in total blocks
296
+ pagesBlocks.forEach((pageBlock: any) => {
297
+ const existsInAll = allBlocks.some((b: any) => b.slug === pageBlock.slug)
298
+ expect(existsInAll).to.be.true
299
+ })
300
+
301
+ postsBlocks.forEach((postBlock: any) => {
302
+ const existsInAll = allBlocks.some((b: any) => b.slug === postBlock.slug)
303
+ expect(existsInAll).to.be.true
304
+ })
305
+
306
+ cy.log('Scope consistency verified across all requests')
307
+ })
308
+ })
309
+ })
310
+ })
311
+
312
+ it('BLOCKS_API_101: Should verify scope array structure', () => {
313
+ apiRequest('GET', API_BLOCKS).then((response) => {
314
+ expect(response.status).to.eq(200)
315
+
316
+ // Verify each block's scope is an array of strings
317
+ response.body.data.forEach((block: any) => {
318
+ expect(block.scope).to.exist
319
+ expect(block.scope).to.be.an('array')
320
+
321
+ block.scope.forEach((scopeValue: any) => {
322
+ expect(scopeValue).to.be.a('string')
323
+ // Verify it's a valid scope value
324
+ expect(['pages', 'posts']).to.include(scopeValue)
325
+ })
326
+ })
327
+
328
+ cy.log('All block scopes have valid array structure')
329
+ })
330
+ })
331
+
332
+ it('BLOCKS_API_102: Should verify dual-scope blocks appear in both filters', () => {
333
+ // Get hero block from posts scope
334
+ apiRequest('GET', `${API_BLOCKS}?scope=posts`).then((postsResponse) => {
335
+ const heroInPosts = postsResponse.body.data.find((b: any) => b.slug === 'hero')
336
+
337
+ if (heroInPosts) {
338
+ // If hero is in posts, it might also be in pages
339
+ apiRequest('GET', `${API_BLOCKS}?scope=pages`).then((pagesResponse) => {
340
+ const heroInPages = pagesResponse.body.data.find((b: any) => b.slug === 'hero')
341
+
342
+ if (heroInPages) {
343
+ // Verify hero has both 'pages' and 'posts' in scope
344
+ expect(heroInPosts.scope).to.include('posts')
345
+ expect(heroInPages.scope).to.include('pages')
346
+
347
+ cy.log("Hero block correctly appears in both scopes")
348
+ } else {
349
+ // Hero is posts-only
350
+ expect(heroInPosts.scope).to.deep.equal(['posts'])
351
+ cy.log("Hero block is posts-only")
352
+ }
353
+ })
354
+ }
355
+ })
356
+ })
357
+ })
358
+
359
+ // ============================================================
360
+ // Public Access (Blocks API is publicly accessible)
361
+ // ============================================================
362
+ describe('Public Access', () => {
363
+ it('BLOCKS_API_200: Should allow public access without authentication', { tags: '@security' }, () => {
364
+ /**
365
+ * NOTE: The blocks API is intentionally public to allow frontend
366
+ * applications to fetch block definitions without authentication.
367
+ * This enables page/post editors to load block schemas client-side.
368
+ */
369
+ cy.request({
370
+ method: 'GET',
371
+ url: API_BLOCKS,
372
+ failOnStatusCode: false
373
+ }).then((response) => {
374
+ expect(response.status).to.eq(200)
375
+ expect(response.body.success).to.be.true
376
+ expect(response.body.data).to.be.an('array')
377
+
378
+ cy.log('Blocks API is publicly accessible (by design)')
379
+ })
380
+ })
381
+
382
+ it('BLOCKS_API_201: Should allow scope filter without authentication', { tags: '@security' }, () => {
383
+ cy.request({
384
+ method: 'GET',
385
+ url: `${API_BLOCKS}?scope=pages`,
386
+ failOnStatusCode: false
387
+ }).then((response) => {
388
+ expect(response.status).to.eq(200)
389
+ expect(response.body.success).to.be.true
390
+ expect(response.body.data).to.be.an('array')
391
+
392
+ cy.log('Blocks scope filter is publicly accessible (by design)')
393
+ })
394
+ })
395
+ })
396
+ })