@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,610 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Post-Categories API - CRUD Tests
5
+ *
6
+ * Test suite for Post-Categories API endpoints (abstracts taxonomies internally).
7
+ * Tests GET, POST, PUT, DELETE operations.
8
+ *
9
+ * Entity characteristics:
10
+ * - Required fields: name
11
+ * - Optional fields: slug (auto-generated if not provided), description, icon, color, parentId
12
+ * - Unique constraint: type + slug (internally type='post_category')
13
+ * - Access: authenticated users only (dual auth: session + API key)
14
+ * - Hierarchical support: parentId for nested categories
15
+ * - Soft delete: deletedAt when category has posts
16
+ *
17
+ * Tags: @api, @feat-posts, @crud, @categories
18
+ */
19
+
20
+ import * as allure from 'allure-cypress'
21
+
22
+ describe('Post-Categories API - CRUD Operations', {
23
+ tags: ['@api', '@feat-posts', '@crud', '@categories']
24
+ }, () => {
25
+ // Test constants
26
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
27
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
28
+ const API_CATEGORIES = `${BASE_URL}/api/v1/post-categories`
29
+
30
+ // Track created categories for cleanup
31
+ let createdCategories: string[] = []
32
+
33
+ // Helper to make authenticated API key requests
34
+ const apiRequest = (method: string, url: string, body?: object) => {
35
+ return cy.request({
36
+ method,
37
+ url,
38
+ headers: {
39
+ 'x-api-key': SUPERADMIN_API_KEY,
40
+ 'Content-Type': 'application/json'
41
+ },
42
+ body,
43
+ failOnStatusCode: false
44
+ })
45
+ }
46
+
47
+ beforeEach(() => {
48
+ allure.epic('API')
49
+ allure.feature('Post Categories')
50
+ })
51
+
52
+ afterEach(() => {
53
+ // Cleanup: Delete categories created during tests
54
+ createdCategories.forEach((categoryId) => {
55
+ apiRequest('DELETE', `${API_CATEGORIES}/${categoryId}`)
56
+ })
57
+ createdCategories = []
58
+ })
59
+
60
+ // ============================================================
61
+ // GET /api/v1/post-categories - List Categories
62
+ // ============================================================
63
+ describe('GET /api/v1/post-categories - List Categories', () => {
64
+ it('CAT_API_001: Should list categories with valid API key', { tags: '@smoke' }, () => {
65
+ allure.story('CRUD Operations')
66
+ allure.severity('critical')
67
+
68
+ apiRequest('GET', API_CATEGORIES).then((response) => {
69
+ expect(response.status).to.eq(200)
70
+ expect(response.body.success).to.be.true
71
+ expect(response.body.data).to.be.an('array')
72
+
73
+ cy.log(`Found ${response.body.data.length} categories`)
74
+ })
75
+ })
76
+
77
+ it('CAT_API_002: Should return category with expected structure', () => {
78
+ apiRequest('GET', API_CATEGORIES).then((response) => {
79
+ expect(response.status).to.eq(200)
80
+
81
+ if (response.body.data.length > 0) {
82
+ const category = response.body.data[0]
83
+
84
+ // Verify category structure (type is not included in list responses)
85
+ expect(category).to.have.property('id')
86
+ expect(category).to.have.property('slug')
87
+ expect(category).to.have.property('name')
88
+ expect(category).to.have.property('isActive')
89
+
90
+ cy.log(`Category structure verified: ${category.name}`)
91
+ }
92
+ })
93
+ })
94
+
95
+ it('CAT_API_003: Should only return active categories by default', () => {
96
+ apiRequest('GET', API_CATEGORIES).then((response) => {
97
+ expect(response.status).to.eq(200)
98
+
99
+ // All returned categories should be active
100
+ response.body.data.forEach((category: any) => {
101
+ expect(category.isActive).to.be.true
102
+ })
103
+
104
+ cy.log(`All ${response.body.data.length} categories are active`)
105
+ })
106
+ })
107
+
108
+ it('CAT_API_004: Should allow public read access without authentication', { tags: '@security' }, () => {
109
+ /**
110
+ * NOTE: GET /api/v1/post-categories is publicly accessible
111
+ * to allow frontend applications to fetch categories for filtering.
112
+ * Only write operations (POST, PUT, DELETE) require authentication.
113
+ */
114
+ cy.request({
115
+ method: 'GET',
116
+ url: API_CATEGORIES,
117
+ failOnStatusCode: false
118
+ }).then((response) => {
119
+ expect(response.status).to.eq(200)
120
+ expect(response.body.success).to.be.true
121
+ expect(response.body.data).to.be.an('array')
122
+
123
+ cy.log('Categories list is publicly accessible (by design)')
124
+ })
125
+ })
126
+ })
127
+
128
+ // ============================================================
129
+ // POST /api/v1/post-categories - Create Category
130
+ // ============================================================
131
+ describe('POST /api/v1/post-categories - Create Category', () => {
132
+ it('CAT_API_010: Should create category with valid data', { tags: '@smoke' }, () => {
133
+ allure.story('CRUD Operations')
134
+ allure.severity('critical')
135
+
136
+ const timestamp = Date.now()
137
+ const categoryData = {
138
+ name: `Test Category ${timestamp}`,
139
+ slug: `test-category-${timestamp}`,
140
+ description: 'Test category description',
141
+ icon: 'bookmark',
142
+ color: '#FF5733'
143
+ }
144
+
145
+ apiRequest('POST', API_CATEGORIES, categoryData).then((response) => {
146
+ expect(response.status).to.eq(201)
147
+ expect(response.body.success).to.be.true
148
+
149
+ const category = response.body.data
150
+ expect(category).to.have.property('id')
151
+ expect(category.type).to.eq('post_category')
152
+ expect(category.slug).to.eq(categoryData.slug)
153
+ expect(category.name).to.eq(categoryData.name)
154
+ expect(category.description).to.eq(categoryData.description)
155
+ expect(category.icon).to.eq(categoryData.icon)
156
+ expect(category.color).to.eq(categoryData.color)
157
+
158
+ createdCategories.push(category.id)
159
+ cy.log(`Created category: ${category.name} (ID: ${category.id})`)
160
+ })
161
+ })
162
+
163
+ it('CAT_API_011: Should auto-generate slug from name if not provided', () => {
164
+ const categoryData = {
165
+ name: `Auto Slug Category ${Date.now()}`
166
+ // slug not provided
167
+ }
168
+
169
+ apiRequest('POST', API_CATEGORIES, categoryData).then((response) => {
170
+ expect(response.status).to.eq(201)
171
+
172
+ const category = response.body.data
173
+ expect(category.slug).to.exist
174
+ expect(category.slug).to.match(/^[a-z0-9\-]+$/)
175
+ expect(category.name).to.eq(categoryData.name)
176
+
177
+ createdCategories.push(category.id)
178
+ cy.log(`Created category with auto-generated slug: ${category.slug}`)
179
+ })
180
+ })
181
+
182
+ it('CAT_API_012: Should create category with minimal data', () => {
183
+ const minimalData = {
184
+ name: `Minimal Category ${Date.now()}`
185
+ }
186
+
187
+ apiRequest('POST', API_CATEGORIES, minimalData).then((response) => {
188
+ expect(response.status).to.eq(201)
189
+
190
+ const category = response.body.data
191
+ expect(category.name).to.eq(minimalData.name)
192
+ expect(category.slug).to.exist
193
+ expect(category.isActive).to.be.true // Default value
194
+
195
+ createdCategories.push(category.id)
196
+ cy.log(`Created minimal category: ${category.id}`)
197
+ })
198
+ })
199
+
200
+ it('CAT_API_013: Should create category with icon and color', () => {
201
+ const categoryData = {
202
+ name: `Styled Category ${Date.now()}`,
203
+ icon: 'star',
204
+ color: '#3B82F6'
205
+ }
206
+
207
+ apiRequest('POST', API_CATEGORIES, categoryData).then((response) => {
208
+ expect(response.status).to.eq(201)
209
+
210
+ const category = response.body.data
211
+ expect(category.icon).to.eq(categoryData.icon)
212
+ expect(category.color).to.eq(categoryData.color)
213
+
214
+ createdCategories.push(category.id)
215
+ cy.log(`Created styled category with icon '${category.icon}' and color '${category.color}'`)
216
+ })
217
+ })
218
+
219
+ it('CAT_API_014: Should reject creation without name', () => {
220
+ const invalidData = {
221
+ slug: `no-name-${Date.now()}`
222
+ // Missing: name
223
+ }
224
+
225
+ apiRequest('POST', API_CATEGORIES, invalidData).then((response) => {
226
+ expect(response.status).to.eq(400)
227
+ expect(response.body.success).to.be.false
228
+
229
+ cy.log('Creation without name rejected with 400')
230
+ })
231
+ })
232
+
233
+ it('CAT_API_015: Should reject invalid slug format', () => {
234
+ const invalidData = {
235
+ name: 'Test Category',
236
+ slug: 'Invalid Slug With Spaces!'
237
+ }
238
+
239
+ apiRequest('POST', API_CATEGORIES, invalidData).then((response) => {
240
+ expect(response.status).to.eq(400)
241
+ expect(response.body.success).to.be.false
242
+
243
+ cy.log('Invalid slug format rejected with 400')
244
+ })
245
+ })
246
+
247
+ it('CAT_API_016: Should reject duplicate slug', () => {
248
+ const uniqueSlug = `duplicate-cat-${Date.now()}`
249
+
250
+ // Create first category
251
+ const categoryData1 = {
252
+ name: 'First Category',
253
+ slug: uniqueSlug
254
+ }
255
+
256
+ apiRequest('POST', API_CATEGORIES, categoryData1).then((response1) => {
257
+ expect(response1.status).to.eq(201)
258
+ createdCategories.push(response1.body.data.id)
259
+
260
+ // Try to create duplicate
261
+ const categoryData2 = {
262
+ name: 'Second Category',
263
+ slug: uniqueSlug // Same slug
264
+ }
265
+
266
+ apiRequest('POST', API_CATEGORIES, categoryData2).then((response2) => {
267
+ expect(response2.status).to.be.oneOf([400, 409])
268
+ expect(response2.body.success).to.be.false
269
+
270
+ cy.log('Duplicate slug rejected')
271
+ })
272
+ })
273
+ })
274
+
275
+ it('CAT_API_017: Should create hierarchical category with parentId', () => {
276
+ // First create a parent category
277
+ const parentData = {
278
+ name: `Parent Category ${Date.now()}`
279
+ }
280
+
281
+ apiRequest('POST', API_CATEGORIES, parentData).then((parentResponse) => {
282
+ expect(parentResponse.status).to.eq(201)
283
+ const parentId = parentResponse.body.data.id
284
+ createdCategories.push(parentId)
285
+
286
+ // Create child category
287
+ const childData = {
288
+ name: `Child Category ${Date.now()}`,
289
+ parentId: parentId
290
+ }
291
+
292
+ apiRequest('POST', API_CATEGORIES, childData).then((childResponse) => {
293
+ expect(childResponse.status).to.eq(201)
294
+
295
+ const childCategory = childResponse.body.data
296
+ expect(childCategory.parentId).to.eq(parentId)
297
+
298
+ createdCategories.push(childCategory.id)
299
+ cy.log(`Created hierarchical category: ${childCategory.name} (parent: ${parentId})`)
300
+ })
301
+ })
302
+ })
303
+
304
+ it('CAT_API_018: Should reject creation without authentication', { tags: '@security' }, () => {
305
+ const categoryData = {
306
+ name: 'Unauthorized Category'
307
+ }
308
+
309
+ cy.request({
310
+ method: 'POST',
311
+ url: API_CATEGORIES,
312
+ body: categoryData,
313
+ failOnStatusCode: false
314
+ }).then((response) => {
315
+ expect(response.status).to.eq(401)
316
+ expect(response.body.success).to.be.false
317
+
318
+ cy.log('Creation without authentication rejected with 401')
319
+ })
320
+ })
321
+ })
322
+
323
+ // ============================================================
324
+ // GET /api/v1/post-categories/{id} - Get Category by ID
325
+ // ============================================================
326
+ describe('GET /api/v1/post-categories/{id} - Get Category by ID', () => {
327
+ let testCategoryId: string
328
+
329
+ beforeEach(() => {
330
+ // Create a test category
331
+ const categoryData = {
332
+ name: `Test Get By ID ${Date.now()}`,
333
+ description: 'Test category for GET by ID'
334
+ }
335
+
336
+ apiRequest('POST', API_CATEGORIES, categoryData).then((response) => {
337
+ testCategoryId = response.body.data.id
338
+ createdCategories.push(testCategoryId)
339
+ })
340
+ })
341
+
342
+ it('CAT_API_020: Should get category by valid ID', () => {
343
+ cy.then(() => {
344
+ apiRequest('GET', `${API_CATEGORIES}/${testCategoryId}`).then((response) => {
345
+ expect(response.status).to.eq(200)
346
+ expect(response.body.success).to.be.true
347
+ expect(response.body.data.id).to.eq(testCategoryId)
348
+ // Note: type field is not included in GET by ID response
349
+ expect(response.body.data).to.have.property('name')
350
+ expect(response.body.data).to.have.property('slug')
351
+
352
+ cy.log(`Got category: ${response.body.data.name}`)
353
+ })
354
+ })
355
+ })
356
+
357
+ it('CAT_API_021: Should return 404 for non-existent category', () => {
358
+ apiRequest('GET', `${API_CATEGORIES}/non-existent-id-12345`).then((response) => {
359
+ expect(response.status).to.eq(404)
360
+ expect(response.body.success).to.be.false
361
+
362
+ cy.log('Non-existent category returns 404')
363
+ })
364
+ })
365
+ })
366
+
367
+ // ============================================================
368
+ // PUT /api/v1/post-categories/{id} - Update Category
369
+ // ============================================================
370
+ describe('PUT /api/v1/post-categories/{id} - Update Category', () => {
371
+ let testCategoryId: string
372
+
373
+ beforeEach(() => {
374
+ const categoryData = {
375
+ name: `Test Update ${Date.now()}`,
376
+ description: 'Original description'
377
+ }
378
+
379
+ apiRequest('POST', API_CATEGORIES, categoryData).then((response) => {
380
+ testCategoryId = response.body.data.id
381
+ createdCategories.push(testCategoryId)
382
+ })
383
+ })
384
+
385
+ it('CAT_API_030: Should update category name', () => {
386
+ const updateData = {
387
+ name: 'Updated Category Name'
388
+ }
389
+
390
+ cy.then(() => {
391
+ apiRequest('PUT', `${API_CATEGORIES}/${testCategoryId}`, updateData).then((response) => {
392
+ expect(response.status).to.eq(200)
393
+ expect(response.body.data.name).to.eq(updateData.name)
394
+
395
+ cy.log(`Updated category name to: ${updateData.name}`)
396
+ })
397
+ })
398
+ })
399
+
400
+ it('CAT_API_031: Should update category slug', () => {
401
+ const updateData = {
402
+ slug: `updated-slug-${Date.now()}`
403
+ }
404
+
405
+ cy.then(() => {
406
+ apiRequest('PUT', `${API_CATEGORIES}/${testCategoryId}`, updateData).then((response) => {
407
+ expect(response.status).to.eq(200)
408
+ expect(response.body.data.slug).to.eq(updateData.slug)
409
+
410
+ cy.log(`Updated category slug to: ${updateData.slug}`)
411
+ })
412
+ })
413
+ })
414
+
415
+ it('CAT_API_032: Should update category description', () => {
416
+ const updateData = {
417
+ description: 'Updated description text'
418
+ }
419
+
420
+ cy.then(() => {
421
+ apiRequest('PUT', `${API_CATEGORIES}/${testCategoryId}`, updateData).then((response) => {
422
+ expect(response.status).to.eq(200)
423
+ expect(response.body.data.description).to.eq(updateData.description)
424
+
425
+ cy.log('Updated category description')
426
+ })
427
+ })
428
+ })
429
+
430
+ it('CAT_API_033: Should update category icon and color', () => {
431
+ const updateData = {
432
+ icon: 'heart',
433
+ color: '#EF4444'
434
+ }
435
+
436
+ cy.then(() => {
437
+ apiRequest('PUT', `${API_CATEGORIES}/${testCategoryId}`, updateData).then((response) => {
438
+ expect(response.status).to.eq(200)
439
+ expect(response.body.data.icon).to.eq(updateData.icon)
440
+ expect(response.body.data.color).to.eq(updateData.color)
441
+
442
+ cy.log(`Updated icon to '${updateData.icon}' and color to '${updateData.color}'`)
443
+ })
444
+ })
445
+ })
446
+
447
+ it('CAT_API_034: Should toggle isActive status', () => {
448
+ const updateData = {
449
+ isActive: false
450
+ }
451
+
452
+ cy.then(() => {
453
+ apiRequest('PUT', `${API_CATEGORIES}/${testCategoryId}`, updateData).then((response) => {
454
+ expect(response.status).to.eq(200)
455
+ expect(response.body.data.isActive).to.be.false
456
+
457
+ cy.log('Category deactivated')
458
+ })
459
+ })
460
+ })
461
+
462
+ it('CAT_API_035: Should return 404 for non-existent category', () => {
463
+ apiRequest('PUT', `${API_CATEGORIES}/non-existent-id-12345`, {
464
+ name: 'New Name'
465
+ }).then((response) => {
466
+ expect(response.status).to.eq(404)
467
+ expect(response.body.success).to.be.false
468
+
469
+ cy.log('Update non-existent category returns 404')
470
+ })
471
+ })
472
+
473
+ it('CAT_API_036: Should reject update without authentication', { tags: '@security' }, () => {
474
+ cy.then(() => {
475
+ cy.request({
476
+ method: 'PUT',
477
+ url: `${API_CATEGORIES}/${testCategoryId}`,
478
+ body: { name: 'Unauthorized Update' },
479
+ failOnStatusCode: false
480
+ }).then((response) => {
481
+ expect(response.status).to.eq(401)
482
+ expect(response.body.success).to.be.false
483
+
484
+ cy.log('Update without authentication rejected with 401')
485
+ })
486
+ })
487
+ })
488
+ })
489
+
490
+ // ============================================================
491
+ // DELETE /api/v1/post-categories/{id} - Delete Category
492
+ // ============================================================
493
+ describe('DELETE /api/v1/post-categories/{id} - Delete Category', () => {
494
+ it('CAT_API_040: Should delete category by valid ID', () => {
495
+ // Create category to delete
496
+ const categoryData = {
497
+ name: `Test Delete ${Date.now()}`
498
+ }
499
+
500
+ apiRequest('POST', API_CATEGORIES, categoryData).then((createResponse) => {
501
+ const categoryId = createResponse.body.data.id
502
+
503
+ // Delete the category
504
+ apiRequest('DELETE', `${API_CATEGORIES}/${categoryId}`).then((deleteResponse) => {
505
+ expect(deleteResponse.status).to.eq(200)
506
+ expect(deleteResponse.body.success).to.be.true
507
+
508
+ // Verify deletion (may be soft delete or hard delete depending on usage)
509
+ apiRequest('GET', `${API_CATEGORIES}/${categoryId}`).then((getResponse) => {
510
+ // Should either be 404 (hard delete) or exist but deletedAt set (soft delete)
511
+ expect(getResponse.status).to.be.oneOf([200, 404])
512
+ cy.log(`Deleted category: ${categoryId}`)
513
+ })
514
+ })
515
+ })
516
+ })
517
+
518
+ it('CAT_API_041: Should return 404 for non-existent category', () => {
519
+ apiRequest('DELETE', `${API_CATEGORIES}/non-existent-id-12345`).then((response) => {
520
+ expect(response.status).to.eq(404)
521
+ expect(response.body.success).to.be.false
522
+
523
+ cy.log('Delete non-existent category returns 404')
524
+ })
525
+ })
526
+
527
+ it('CAT_API_042: Should reject deletion without authentication', { tags: '@security' }, () => {
528
+ // Create a category first
529
+ const categoryData = {
530
+ name: `Auth Delete Test ${Date.now()}`
531
+ }
532
+
533
+ apiRequest('POST', API_CATEGORIES, categoryData).then((createResponse) => {
534
+ const categoryId = createResponse.body.data.id
535
+ createdCategories.push(categoryId)
536
+
537
+ // Try to delete without auth
538
+ cy.request({
539
+ method: 'DELETE',
540
+ url: `${API_CATEGORIES}/${categoryId}`,
541
+ failOnStatusCode: false
542
+ }).then((response) => {
543
+ expect(response.status).to.eq(401)
544
+ expect(response.body.success).to.be.false
545
+
546
+ cy.log('Deletion without authentication rejected with 401')
547
+ })
548
+ })
549
+ })
550
+ })
551
+
552
+ // ============================================================
553
+ // Integration Test - Complete CRUD Lifecycle
554
+ // ============================================================
555
+ describe('Integration - Complete CRUD Lifecycle', () => {
556
+ it('CAT_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {
557
+ const timestamp = Date.now()
558
+ const categoryData = {
559
+ name: `Lifecycle Test ${timestamp}`,
560
+ slug: `lifecycle-test-${timestamp}`,
561
+ description: 'Lifecycle test description',
562
+ icon: 'check',
563
+ color: '#10B981'
564
+ }
565
+
566
+ // 1. CREATE
567
+ apiRequest('POST', API_CATEGORIES, categoryData).then((createResponse) => {
568
+ expect(createResponse.status).to.eq(201)
569
+ const createdCategory = createResponse.body.data
570
+ cy.log(`1. Created category: ${createdCategory.id}`)
571
+
572
+ // 2. READ
573
+ apiRequest('GET', `${API_CATEGORIES}/${createdCategory.id}`).then((readResponse) => {
574
+ expect(readResponse.status).to.eq(200)
575
+ expect(readResponse.body.data.name).to.eq(categoryData.name)
576
+ cy.log(`2. Read category: ${createdCategory.id}`)
577
+
578
+ // 3. UPDATE
579
+ const updateData = {
580
+ name: 'Updated Lifecycle Category',
581
+ description: 'Updated description',
582
+ icon: 'star',
583
+ color: '#F59E0B'
584
+ }
585
+ apiRequest('PUT', `${API_CATEGORIES}/${createdCategory.id}`, updateData).then((updateResponse) => {
586
+ expect(updateResponse.status).to.eq(200)
587
+ expect(updateResponse.body.data.name).to.eq(updateData.name)
588
+ expect(updateResponse.body.data.description).to.eq(updateData.description)
589
+ expect(updateResponse.body.data.icon).to.eq(updateData.icon)
590
+ expect(updateResponse.body.data.color).to.eq(updateData.color)
591
+ cy.log(`3. Updated category: ${updateData.name}`)
592
+
593
+ // 4. DELETE
594
+ apiRequest('DELETE', `${API_CATEGORIES}/${createdCategory.id}`).then((deleteResponse) => {
595
+ expect(deleteResponse.status).to.eq(200)
596
+ cy.log(`4. Deleted category: ${createdCategory.id}`)
597
+
598
+ // 5. VERIFY DELETION
599
+ apiRequest('GET', `${API_CATEGORIES}/${createdCategory.id}`).then((finalResponse) => {
600
+ // May return 404 or 200 with deletedAt depending on implementation
601
+ cy.log('5. Verified deletion')
602
+ cy.log('Full CRUD lifecycle completed successfully')
603
+ })
604
+ })
605
+ })
606
+ })
607
+ })
608
+ })
609
+ })
610
+ })