@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,602 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Tasks API - CRUD Tests
5
+ *
6
+ * Basic CRUD operations for /api/v1/tasks endpoints
7
+ * Uses superadmin API key for full access with team context
8
+ */
9
+
10
+ import * as allure from 'allure-cypress'
11
+
12
+ const TaskAPIController = require('../../../src/controllers/TaskAPIController.js')
13
+
14
+ describe('Tasks API - CRUD Operations', {
15
+ tags: ['@api', '@feat-tasks', '@crud', '@regression']
16
+ }, () => {
17
+ let taskAPI: any
18
+ let createdTasks: any[] = []
19
+
20
+ // Superadmin API key for testing
21
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
22
+ const TEAM_ID = 'team-tmt-001'
23
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
24
+
25
+ before(() => {
26
+ // Initialize API controller with superadmin API key and team context
27
+ taskAPI = new TaskAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
28
+ cy.log('TaskAPIController initialized')
29
+ cy.log(`Base URL: ${BASE_URL}`)
30
+ cy.log(`Team ID: ${TEAM_ID}`)
31
+ })
32
+
33
+ beforeEach(() => {
34
+ allure.epic('API')
35
+ allure.feature('Tasks')
36
+ })
37
+
38
+ afterEach(() => {
39
+ // Cleanup: Delete tasks created during tests
40
+ if (createdTasks.length > 0) {
41
+ createdTasks.forEach((task: any) => {
42
+ if (task && task.id) {
43
+ taskAPI.deleteTask(task.id)
44
+ }
45
+ })
46
+ createdTasks = []
47
+ }
48
+ })
49
+
50
+ // ============================================================
51
+ // GET /api/v1/tasks - List Tasks
52
+ // ============================================================
53
+ describe('GET /api/v1/tasks - List Tasks', () => {
54
+ it('TASKS_API_001: Should list tasks with valid API key', { tags: '@smoke' }, () => {
55
+ allure.story('CRUD Operations')
56
+ allure.severity('critical')
57
+ taskAPI.getTasks().then((response: any) => {
58
+ taskAPI.validateSuccessResponse(response, 200)
59
+ taskAPI.validatePaginatedResponse(response)
60
+ expect(response.body.data).to.be.an('array')
61
+
62
+ cy.log(`Found ${response.body.data.length} tasks`)
63
+ cy.log(`Total tasks: ${response.body.info.total}`)
64
+ })
65
+ })
66
+
67
+ it('TASKS_API_002: Should list tasks with pagination', () => {
68
+ taskAPI.getTasks({ page: 1, limit: 5 }).then((response: any) => {
69
+ taskAPI.validatePaginatedResponse(response)
70
+ expect(response.body.info.page).to.eq(1)
71
+ expect(response.body.info.limit).to.eq(5)
72
+ expect(response.body.data.length).to.be.at.most(5)
73
+
74
+ cy.log(`Page 1, Limit 5: Got ${response.body.data.length} tasks`)
75
+ })
76
+ })
77
+
78
+ it('TASKS_API_003: Should filter tasks by status', () => {
79
+ // First create a task with known status
80
+ const taskData = taskAPI.generateRandomTaskData({ status: 'done' })
81
+
82
+ taskAPI.createTask(taskData).then((createResponse: any) => {
83
+ expect(createResponse.status).to.eq(201)
84
+ createdTasks.push(createResponse.body.data)
85
+
86
+ // Now filter by status
87
+ taskAPI.getTasks({ status: 'done' }).then((response: any) => {
88
+ taskAPI.validateSuccessResponse(response, 200)
89
+
90
+ // All returned tasks should have status 'done'
91
+ response.body.data.forEach((task: any) => {
92
+ expect(task.status).to.eq('done')
93
+ })
94
+
95
+ cy.log(`Found ${response.body.data.length} tasks with status 'done'`)
96
+ })
97
+ })
98
+ })
99
+
100
+ it('TASKS_API_004: Should filter tasks by priority', () => {
101
+ // First create a task with known priority
102
+ const taskData = taskAPI.generateRandomTaskData({ priority: 'high' })
103
+
104
+ taskAPI.createTask(taskData).then((createResponse: any) => {
105
+ expect(createResponse.status).to.eq(201)
106
+ createdTasks.push(createResponse.body.data)
107
+
108
+ // Now filter by priority
109
+ taskAPI.getTasks({ priority: 'high' }).then((response: any) => {
110
+ taskAPI.validateSuccessResponse(response, 200)
111
+
112
+ // All returned tasks should have priority 'high'
113
+ response.body.data.forEach((task: any) => {
114
+ expect(task.priority).to.eq('high')
115
+ })
116
+
117
+ cy.log(`Found ${response.body.data.length} tasks with priority 'high'`)
118
+ })
119
+ })
120
+ })
121
+
122
+ it('TASKS_API_005: Should search tasks by title/description', () => {
123
+ // Create a task with a unique searchable term
124
+ const uniqueTerm = `SearchTest${Date.now()}`
125
+ const taskData = taskAPI.generateRandomTaskData({
126
+ title: `Task with ${uniqueTerm} in title`,
127
+ description: 'Searchable task for testing'
128
+ })
129
+
130
+ taskAPI.createTask(taskData).then((createResponse: any) => {
131
+ expect(createResponse.status).to.eq(201)
132
+ createdTasks.push(createResponse.body.data)
133
+
134
+ // Search for the unique term
135
+ taskAPI.getTasks({ search: uniqueTerm }).then((response: any) => {
136
+ taskAPI.validateSuccessResponse(response, 200)
137
+ expect(response.body.data).to.be.an('array')
138
+ expect(response.body.data.length).to.be.greaterThan(0)
139
+
140
+ // Verify the found task contains our search term
141
+ const foundTask = response.body.data.find((t: any) => t.id === createResponse.body.data.id)
142
+ expect(foundTask).to.exist
143
+ expect(foundTask.title).to.include(uniqueTerm)
144
+
145
+ cy.log(`Search found ${response.body.data.length} tasks matching '${uniqueTerm}'`)
146
+ })
147
+ })
148
+ })
149
+
150
+ it('TASKS_API_006: Should return empty array for non-matching search', () => {
151
+ const nonExistentTerm = 'NonExistentSearchTerm123456789'
152
+
153
+ taskAPI.getTasks({ search: nonExistentTerm }).then((response: any) => {
154
+ taskAPI.validateSuccessResponse(response, 200)
155
+ expect(response.body.data).to.be.an('array')
156
+ expect(response.body.data.length).to.eq(0)
157
+
158
+ cy.log('Search with non-matching term returns empty array')
159
+ })
160
+ })
161
+
162
+ it('TASKS_API_007: Should reject request without API key', () => {
163
+ const originalApiKey = taskAPI.apiKey
164
+ taskAPI.setApiKey(null)
165
+
166
+ taskAPI.getTasks().then((response: any) => {
167
+ expect(response.status).to.eq(401)
168
+ expect(response.body.success).to.be.false
169
+ })
170
+
171
+ taskAPI.setApiKey(originalApiKey)
172
+ })
173
+
174
+ it('TASKS_API_008: Should reject request without x-team-id', () => {
175
+ const originalTeamId = taskAPI.teamId
176
+ taskAPI.setTeamId(null)
177
+
178
+ taskAPI.getTasks().then((response: any) => {
179
+ expect(response.status).to.eq(400)
180
+ expect(response.body.success).to.be.false
181
+ expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
182
+ })
183
+
184
+ taskAPI.setTeamId(originalTeamId)
185
+ })
186
+ })
187
+
188
+ // ============================================================
189
+ // POST /api/v1/tasks - Create Task
190
+ // ============================================================
191
+ describe('POST /api/v1/tasks - Create Task', () => {
192
+ it('TASKS_API_010: Should create task with valid data', { tags: '@smoke' }, () => {
193
+ allure.story('CRUD Operations')
194
+ allure.severity('critical')
195
+ const taskData = taskAPI.generateRandomTaskData({
196
+ title: 'TestCreate - Full Task',
197
+ description: 'A complete task with all fields',
198
+ status: 'in-progress',
199
+ priority: 'high',
200
+ tags: ['testing', 'cypress'],
201
+ dueDate: '2025-12-31',
202
+ estimatedHours: 8.5
203
+ })
204
+
205
+ taskAPI.createTask(taskData).then((response: any) => {
206
+ taskAPI.validateSuccessResponse(response, 201)
207
+ taskAPI.validateTaskObject(response.body.data)
208
+
209
+ expect(response.body.data.title).to.eq(taskData.title)
210
+ expect(response.body.data.description).to.eq(taskData.description)
211
+ expect(response.body.data.status).to.eq(taskData.status)
212
+ expect(response.body.data.priority).to.eq(taskData.priority)
213
+ expect(response.body.data.tags).to.deep.eq(taskData.tags)
214
+
215
+ // Save for cleanup
216
+ createdTasks.push(response.body.data)
217
+
218
+ cy.log(`Created task: ${response.body.data.title}`)
219
+ })
220
+ })
221
+
222
+ it('TASKS_API_011: Should create task with minimal data and default values', () => {
223
+ const taskData = {
224
+ title: `Minimal Task ${Date.now()}`
225
+ }
226
+
227
+ taskAPI.createTask(taskData).then((response: any) => {
228
+ taskAPI.validateSuccessResponse(response, 201)
229
+ taskAPI.validateTaskObject(response.body.data)
230
+
231
+ // Verify default values
232
+ expect(response.body.data.title).to.eq(taskData.title)
233
+ expect(response.body.data.status).to.eq('todo')
234
+ expect(response.body.data.priority).to.eq('medium')
235
+ expect(response.body.data.completed).to.be.false
236
+ expect(response.body.data.tags).to.deep.eq([])
237
+
238
+ createdTasks.push(response.body.data)
239
+ cy.log(`Created task with defaults: ${response.body.data.id}`)
240
+ })
241
+ })
242
+
243
+ it('TASKS_API_012: Should create task with tags', () => {
244
+ const taskData = taskAPI.generateRandomTaskData({
245
+ tags: ['feature', 'priority', 'v2']
246
+ })
247
+
248
+ taskAPI.createTask(taskData).then((response: any) => {
249
+ taskAPI.validateSuccessResponse(response, 201)
250
+
251
+ expect(response.body.data.tags).to.be.an('array')
252
+ expect(response.body.data.tags).to.have.length(3)
253
+ expect(response.body.data.tags).to.include('feature')
254
+ expect(response.body.data.tags).to.include('priority')
255
+ expect(response.body.data.tags).to.include('v2')
256
+
257
+ createdTasks.push(response.body.data)
258
+ cy.log(`Created task with tags: ${response.body.data.tags.join(', ')}`)
259
+ })
260
+ })
261
+
262
+ it('TASKS_API_013: Should reject creation without title', () => {
263
+ const taskData = {
264
+ description: 'Task without title',
265
+ status: 'todo'
266
+ }
267
+
268
+ taskAPI.createTask(taskData).then((response: any) => {
269
+ expect(response.status).to.eq(400)
270
+ expect(response.body.success).to.be.false
271
+ })
272
+ })
273
+
274
+ it('TASKS_API_014: Should reject creation with invalid status', () => {
275
+ const taskData = taskAPI.generateRandomTaskData({
276
+ status: 'invalid-status'
277
+ })
278
+
279
+ taskAPI.createTask(taskData).then((response: any) => {
280
+ expect(response.status).to.eq(400)
281
+ expect(response.body.success).to.be.false
282
+ })
283
+ })
284
+
285
+ it('TASKS_API_015: Should reject creation with invalid priority', () => {
286
+ const taskData = taskAPI.generateRandomTaskData({
287
+ priority: 'invalid-priority'
288
+ })
289
+
290
+ taskAPI.createTask(taskData).then((response: any) => {
291
+ expect(response.status).to.eq(400)
292
+ expect(response.body.success).to.be.false
293
+ })
294
+ })
295
+
296
+ it('TASKS_API_016: Should reject creation without x-team-id', () => {
297
+ const originalTeamId = taskAPI.teamId
298
+ taskAPI.setTeamId(null)
299
+
300
+ const taskData = taskAPI.generateRandomTaskData()
301
+
302
+ taskAPI.createTask(taskData).then((response: any) => {
303
+ expect(response.status).to.eq(400)
304
+ expect(response.body.success).to.be.false
305
+ expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
306
+ })
307
+
308
+ taskAPI.setTeamId(originalTeamId)
309
+ })
310
+ })
311
+
312
+ // ============================================================
313
+ // GET /api/v1/tasks/{id} - Get Task by ID
314
+ // ============================================================
315
+ describe('GET /api/v1/tasks/{id} - Get Task by ID', () => {
316
+ let testTask: any
317
+
318
+ beforeEach(() => {
319
+ // Create a test task for each test
320
+ const taskData = taskAPI.generateRandomTaskData({
321
+ title: 'TestGetById Task'
322
+ })
323
+
324
+ taskAPI.createTask(taskData).then((response: any) => {
325
+ testTask = response.body.data
326
+ createdTasks.push(testTask)
327
+ })
328
+ })
329
+
330
+ it('TASKS_API_020: Should get task by valid ID', () => {
331
+ cy.then(() => {
332
+ taskAPI.getTaskById(testTask.id).then((response: any) => {
333
+ taskAPI.validateSuccessResponse(response, 200)
334
+ taskAPI.validateTaskObject(response.body.data)
335
+
336
+ expect(response.body.data.id).to.eq(testTask.id)
337
+ expect(response.body.data.title).to.eq(testTask.title)
338
+
339
+ cy.log(`Got task by ID: ${testTask.id}`)
340
+ })
341
+ })
342
+ })
343
+
344
+ it('TASKS_API_021: Should return 404 for non-existent task', () => {
345
+ const nonExistentId = 'non-existent-task-id-12345'
346
+
347
+ taskAPI.getTaskById(nonExistentId).then((response: any) => {
348
+ expect(response.status).to.eq(404)
349
+ expect(response.body.success).to.be.false
350
+ })
351
+ })
352
+
353
+ it.skip('TASKS_API_022: Should reject access to other user\'s task', () => {
354
+ /**
355
+ * SKIPPED: This test cannot be completed with current setup
356
+ *
357
+ * Reason:
358
+ * - Superadmin API key bypasses RLS (is_superadmin() function)
359
+ * - Bug documented in pendings.md §1.1: API keys ignore team member roles
360
+ * - Cannot create API key with 'member' role to test userId isolation
361
+ *
362
+ * When the bug is fixed:
363
+ * - Create API key for member user in different team
364
+ * - Attempt to access task created by superadmin
365
+ * - Should return 404 (filtered by userId within team)
366
+ */
367
+ cy.log('SKIPPED: Cannot test user isolation - superadmin bypasses RLS')
368
+ })
369
+ })
370
+
371
+ // ============================================================
372
+ // PATCH /api/v1/tasks/{id} - Update Task
373
+ // ============================================================
374
+ describe('PATCH /api/v1/tasks/{id} - Update Task', () => {
375
+ let testTask: any
376
+
377
+ beforeEach(() => {
378
+ // Create a test task for each test
379
+ const taskData = taskAPI.generateRandomTaskData({
380
+ title: 'TestUpdate Task',
381
+ status: 'todo',
382
+ priority: 'medium'
383
+ })
384
+
385
+ taskAPI.createTask(taskData).then((response: any) => {
386
+ testTask = response.body.data
387
+ createdTasks.push(testTask)
388
+ })
389
+ })
390
+
391
+ it('TASKS_API_030: Should update task with valid data', () => {
392
+ const updateData = {
393
+ title: 'Updated Task Title',
394
+ description: 'Updated description'
395
+ }
396
+
397
+ cy.then(() => {
398
+ taskAPI.updateTask(testTask.id, updateData).then((response: any) => {
399
+ taskAPI.validateSuccessResponse(response, 200)
400
+ taskAPI.validateTaskObject(response.body.data)
401
+
402
+ expect(response.body.data.title).to.eq(updateData.title)
403
+ expect(response.body.data.description).to.eq(updateData.description)
404
+ expect(response.body.data.id).to.eq(testTask.id)
405
+
406
+ cy.log(`Updated task: ${testTask.id}`)
407
+ })
408
+ })
409
+ })
410
+
411
+ it('TASKS_API_031: Should update task status', () => {
412
+ const updateData = {
413
+ status: 'in-progress'
414
+ }
415
+
416
+ cy.then(() => {
417
+ taskAPI.updateTask(testTask.id, updateData).then((response: any) => {
418
+ taskAPI.validateSuccessResponse(response, 200)
419
+ expect(response.body.data.status).to.eq(updateData.status)
420
+
421
+ cy.log(`Updated task status to: ${updateData.status}`)
422
+ })
423
+ })
424
+ })
425
+
426
+ it('TASKS_API_032: Should update task priority', () => {
427
+ const updateData = {
428
+ priority: 'urgent'
429
+ }
430
+
431
+ cy.then(() => {
432
+ taskAPI.updateTask(testTask.id, updateData).then((response: any) => {
433
+ taskAPI.validateSuccessResponse(response, 200)
434
+ expect(response.body.data.priority).to.eq(updateData.priority)
435
+
436
+ cy.log(`Updated task priority to: ${updateData.priority}`)
437
+ })
438
+ })
439
+ })
440
+
441
+ it('TASKS_API_033: Should update task tags', () => {
442
+ const updateData = {
443
+ tags: ['updated', 'modified', 'test']
444
+ }
445
+
446
+ cy.then(() => {
447
+ taskAPI.updateTask(testTask.id, updateData).then((response: any) => {
448
+ taskAPI.validateSuccessResponse(response, 200)
449
+ expect(response.body.data.tags).to.deep.eq(updateData.tags)
450
+
451
+ cy.log(`Updated task tags: ${updateData.tags.join(', ')}`)
452
+ })
453
+ })
454
+ })
455
+
456
+ it('TASKS_API_034: Should return 404 for non-existent task', () => {
457
+ const nonExistentId = 'non-existent-task-id-12345'
458
+ const updateData = { title: 'Updated' }
459
+
460
+ taskAPI.updateTask(nonExistentId, updateData).then((response: any) => {
461
+ expect(response.status).to.eq(404)
462
+ expect(response.body.success).to.be.false
463
+ })
464
+ })
465
+
466
+ it('TASKS_API_035: Should reject empty update body', () => {
467
+ cy.then(() => {
468
+ taskAPI.updateTask(testTask.id, {}).then((response: any) => {
469
+ expect(response.status).to.eq(400)
470
+ expect(response.body.success).to.be.false
471
+ })
472
+ })
473
+ })
474
+ })
475
+
476
+ // ============================================================
477
+ // DELETE /api/v1/tasks/{id} - Delete Task
478
+ // ============================================================
479
+ describe('DELETE /api/v1/tasks/{id} - Delete Task', () => {
480
+ let testTask: any
481
+
482
+ beforeEach(() => {
483
+ // Create a test task for each test
484
+ const taskData = taskAPI.generateRandomTaskData({
485
+ title: 'TestDelete Task'
486
+ })
487
+
488
+ taskAPI.createTask(taskData).then((response: any) => {
489
+ testTask = response.body.data
490
+ // Don't add to createdTasks - we'll delete manually
491
+ })
492
+ })
493
+
494
+ it('TASKS_API_040: Should delete task by valid ID', () => {
495
+ cy.then(() => {
496
+ taskAPI.deleteTask(testTask.id).then((response: any) => {
497
+ taskAPI.validateSuccessResponse(response, 200)
498
+ // Delete response has data.success and data.id
499
+ expect(response.body.data.success).to.be.true
500
+ expect(response.body.data.id).to.exist
501
+
502
+ cy.log(`Deleted task: ${testTask.id}`)
503
+
504
+ // Verify task was deleted
505
+ taskAPI.getTaskById(testTask.id).then((getResponse: any) => {
506
+ expect(getResponse.status).to.eq(404)
507
+ })
508
+ })
509
+ })
510
+ })
511
+
512
+ it('TASKS_API_041: Should return 404 for non-existent task', () => {
513
+ const nonExistentId = 'non-existent-task-id-12345'
514
+
515
+ taskAPI.deleteTask(nonExistentId).then((response: any) => {
516
+ expect(response.status).to.eq(404)
517
+ expect(response.body.success).to.be.false
518
+ })
519
+
520
+ // Add testTask to cleanup since we didn't delete it
521
+ createdTasks.push(testTask)
522
+ })
523
+
524
+ it('TASKS_API_042: Should verify deletion persists', () => {
525
+ cy.then(() => {
526
+ // Delete the task
527
+ taskAPI.deleteTask(testTask.id).then((deleteResponse: any) => {
528
+ expect(deleteResponse.status).to.eq(200)
529
+
530
+ // Immediate GET should return 404
531
+ taskAPI.getTaskById(testTask.id).then((getResponse1: any) => {
532
+ expect(getResponse1.status).to.eq(404)
533
+
534
+ // Second GET should also return 404 (deletion persisted)
535
+ taskAPI.getTaskById(testTask.id).then((getResponse2: any) => {
536
+ expect(getResponse2.status).to.eq(404)
537
+ cy.log('Verified deletion persists: task not found after multiple GET attempts')
538
+ })
539
+ })
540
+ })
541
+ })
542
+ })
543
+ })
544
+
545
+ // ============================================================
546
+ // Integration Test - Complete CRUD Lifecycle
547
+ // ============================================================
548
+ describe('Integration - Complete CRUD Lifecycle', () => {
549
+ it('TASKS_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {
550
+ const taskData = taskAPI.generateRandomTaskData({
551
+ title: 'Lifecycle Test Task',
552
+ description: 'Testing complete CRUD cycle',
553
+ status: 'todo',
554
+ priority: 'medium'
555
+ })
556
+
557
+ // 1. CREATE
558
+ taskAPI.createTask(taskData).then((createResponse: any) => {
559
+ taskAPI.validateSuccessResponse(createResponse, 201)
560
+ const createdTask = createResponse.body.data
561
+ cy.log(`1. Created task: ${createdTask.id}`)
562
+
563
+ // 2. READ
564
+ taskAPI.getTaskById(createdTask.id).then((readResponse: any) => {
565
+ taskAPI.validateSuccessResponse(readResponse, 200)
566
+ expect(readResponse.body.data.id).to.eq(createdTask.id)
567
+ expect(readResponse.body.data.title).to.eq(taskData.title)
568
+ cy.log(`2. Read task: ${createdTask.id}`)
569
+
570
+ // 3. UPDATE
571
+ const updateData = {
572
+ title: 'Updated Lifecycle Task',
573
+ status: 'in-progress',
574
+ priority: 'high'
575
+ }
576
+ taskAPI.updateTask(createdTask.id, updateData).then((updateResponse: any) => {
577
+ taskAPI.validateSuccessResponse(updateResponse, 200)
578
+ expect(updateResponse.body.data.title).to.eq(updateData.title)
579
+ expect(updateResponse.body.data.status).to.eq(updateData.status)
580
+ expect(updateResponse.body.data.priority).to.eq(updateData.priority)
581
+ cy.log(`3. Updated task: ${updateData.title}`)
582
+
583
+ // 4. DELETE
584
+ taskAPI.deleteTask(createdTask.id).then((deleteResponse: any) => {
585
+ taskAPI.validateSuccessResponse(deleteResponse, 200)
586
+ // Delete response has data.success instead of data.deleted
587
+ expect(deleteResponse.body.data.success).to.be.true
588
+ cy.log(`4. Deleted task: ${createdTask.id}`)
589
+
590
+ // 5. VERIFY DELETION
591
+ taskAPI.getTaskById(createdTask.id).then((finalResponse: any) => {
592
+ expect(finalResponse.status).to.eq(404)
593
+ cy.log(`5. Verified deletion: task not found (404)`)
594
+ cy.log('Full CRUD lifecycle completed successfully')
595
+ })
596
+ })
597
+ })
598
+ })
599
+ })
600
+ })
601
+ })
602
+ })