@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,648 @@
1
+ /**
2
+ * Customers API - CRUD Tests
3
+ *
4
+ * Comprehensive test suite for Customer API endpoints.
5
+ * Tests GET, POST, PATCH, DELETE operations.
6
+ *
7
+ * Entity characteristics:
8
+ * - Required fields: name, account (UNIQUE), office
9
+ * - Access: shared within team (all team members see all customers)
10
+ * - Team context: required (x-team-id header)
11
+ */
12
+
13
+ /// <reference types="cypress" />
14
+
15
+ import * as allure from 'allure-cypress'
16
+
17
+ const CustomerAPIController = require('../../../src/controllers/CustomerAPIController.js')
18
+
19
+ describe('Customers API - CRUD Operations', {
20
+ tags: ['@api', '@feat-customers', '@crud', '@regression']
21
+ }, () => {
22
+ // Test constants
23
+ const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
24
+ const TEAM_ID = 'team-tmt-001'
25
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
26
+
27
+ // Controller instance
28
+ let customerAPI: InstanceType<typeof CustomerAPIController>
29
+
30
+ // Track created customers for cleanup
31
+ let createdCustomers: any[] = []
32
+
33
+ before(() => {
34
+ // Initialize controller with superadmin credentials
35
+ customerAPI = new CustomerAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
36
+ })
37
+
38
+ beforeEach(() => {
39
+ allure.epic('API')
40
+ allure.feature('Customers')
41
+ })
42
+
43
+ afterEach(() => {
44
+ // Cleanup created customers after each test
45
+ createdCustomers.forEach((customer) => {
46
+ if (customer?.id) {
47
+ customerAPI.deleteCustomer(customer.id)
48
+ }
49
+ })
50
+ createdCustomers = []
51
+ })
52
+
53
+ // ============================================
54
+ // GET /api/v1/customers - List Customers
55
+ // ============================================
56
+ describe('GET /api/v1/customers - List Customers', () => {
57
+ it('CUST_API_001: Should list customers with valid API key', { tags: '@smoke' }, () => {
58
+ allure.story('CRUD Operations')
59
+ allure.severity('critical')
60
+ customerAPI.getCustomers().then((response: any) => {
61
+ customerAPI.validateSuccessResponse(response, 200)
62
+ expect(response.body.data).to.be.an('array')
63
+ expect(response.body.info).to.have.property('page')
64
+ expect(response.body.info).to.have.property('limit')
65
+ expect(response.body.info).to.have.property('total')
66
+ expect(response.body.info).to.have.property('totalPages')
67
+
68
+ cy.log(`Found ${response.body.data.length} customers`)
69
+ })
70
+ })
71
+
72
+ it('CUST_API_002: Should list customers with pagination', () => {
73
+ customerAPI.getCustomers({ page: 1, limit: 5 }).then((response: any) => {
74
+ customerAPI.validatePaginatedResponse(response)
75
+ expect(response.body.info.page).to.eq(1)
76
+ expect(response.body.info.limit).to.eq(5)
77
+ expect(response.body.data.length).to.be.at.most(5)
78
+
79
+ cy.log(`Page 1 with limit 5: ${response.body.data.length} customers`)
80
+ })
81
+ })
82
+
83
+ it('CUST_API_003: Should filter customers by office', () => {
84
+ // First create a customer with a specific office
85
+ const testOffice = `TestOffice-${Date.now()}`
86
+ const customerData = customerAPI.generateRandomCustomerData({ office: testOffice })
87
+
88
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
89
+ expect(createResponse.status).to.eq(201)
90
+ createdCustomers.push(createResponse.body.data)
91
+
92
+ // Now filter by that office
93
+ customerAPI.getCustomers({ office: testOffice }).then((response: any) => {
94
+ customerAPI.validateSuccessResponse(response, 200)
95
+ expect(response.body.data).to.be.an('array')
96
+
97
+ // All returned customers should have the specified office
98
+ response.body.data.forEach((customer: any) => {
99
+ expect(customer.office).to.eq(testOffice)
100
+ })
101
+
102
+ cy.log(`Found ${response.body.data.length} customers with office '${testOffice}'`)
103
+ })
104
+ })
105
+ })
106
+
107
+ it('CUST_API_004: Should filter customers by salesRep', () => {
108
+ // First create a customer with a specific salesRep
109
+ const testSalesRep = `SalesRep-${Date.now()}`
110
+ const customerData = customerAPI.generateRandomCustomerData({ salesRep: testSalesRep })
111
+
112
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
113
+ expect(createResponse.status).to.eq(201)
114
+ createdCustomers.push(createResponse.body.data)
115
+
116
+ // Now filter by that salesRep
117
+ customerAPI.getCustomers({ salesRep: testSalesRep }).then((response: any) => {
118
+ customerAPI.validateSuccessResponse(response, 200)
119
+ expect(response.body.data).to.be.an('array')
120
+
121
+ // All returned customers should have the specified salesRep
122
+ response.body.data.forEach((customer: any) => {
123
+ expect(customer.salesRep).to.eq(testSalesRep)
124
+ })
125
+
126
+ cy.log(`Found ${response.body.data.length} customers with salesRep '${testSalesRep}'`)
127
+ })
128
+ })
129
+ })
130
+
131
+ it('CUST_API_005: Should search customers by name/account', () => {
132
+ // Create a customer with a unique searchable term
133
+ const uniqueTerm = `SearchCustomer${Date.now()}`
134
+ const customerData = customerAPI.generateRandomCustomerData({
135
+ name: `Customer ${uniqueTerm} Corp`
136
+ })
137
+
138
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
139
+ expect(createResponse.status).to.eq(201)
140
+ createdCustomers.push(createResponse.body.data)
141
+
142
+ // Search for the unique term
143
+ customerAPI.getCustomers({ search: uniqueTerm }).then((response: any) => {
144
+ customerAPI.validateSuccessResponse(response, 200)
145
+ expect(response.body.data).to.be.an('array')
146
+ expect(response.body.data.length).to.be.greaterThan(0)
147
+
148
+ // Verify the found customer contains our search term
149
+ const foundCustomer = response.body.data.find(
150
+ (c: any) => c.id === createResponse.body.data.id
151
+ )
152
+ expect(foundCustomer).to.exist
153
+ expect(foundCustomer.name).to.include(uniqueTerm)
154
+
155
+ cy.log(`Search found ${response.body.data.length} customers matching '${uniqueTerm}'`)
156
+ })
157
+ })
158
+ })
159
+
160
+ it('CUST_API_006: Should return empty array for non-matching search', () => {
161
+ const nonExistentTerm = 'NonExistentCustomerSearchTerm123456789'
162
+
163
+ customerAPI.getCustomers({ search: nonExistentTerm }).then((response: any) => {
164
+ customerAPI.validateSuccessResponse(response, 200)
165
+ expect(response.body.data).to.be.an('array')
166
+ expect(response.body.data.length).to.eq(0)
167
+
168
+ cy.log('Search with non-matching term returns empty array')
169
+ })
170
+ })
171
+
172
+ it('CUST_API_007: Should reject request without API key', () => {
173
+ const noAuthAPI = new CustomerAPIController(BASE_URL, null, TEAM_ID)
174
+
175
+ noAuthAPI.getCustomers().then((response: any) => {
176
+ expect(response.status).to.eq(401)
177
+ expect(response.body).to.have.property('success', false)
178
+
179
+ cy.log('Request without API key rejected with 401')
180
+ })
181
+ })
182
+
183
+ it('CUST_API_008: Should reject request without x-team-id', () => {
184
+ const noTeamAPI = new CustomerAPIController(BASE_URL, SUPERADMIN_API_KEY, null)
185
+
186
+ noTeamAPI.getCustomers().then((response: any) => {
187
+ expect(response.status).to.eq(400)
188
+ expect(response.body).to.have.property('success', false)
189
+ expect(response.body).to.have.property('code', 'TEAM_CONTEXT_REQUIRED')
190
+
191
+ cy.log('Request without x-team-id rejected with TEAM_CONTEXT_REQUIRED')
192
+ })
193
+ })
194
+ })
195
+
196
+ // ============================================
197
+ // POST /api/v1/customers - Create Customer
198
+ // ============================================
199
+ describe('POST /api/v1/customers - Create Customer', () => {
200
+ it('CUST_API_010: Should create customer with valid data', { tags: '@smoke' }, () => {
201
+ allure.story('CRUD Operations')
202
+ allure.severity('critical')
203
+ const customerData = customerAPI.generateRandomCustomerData({
204
+ phone: '+1-555-1234',
205
+ salesRep: 'John Sales',
206
+ visitDays: ['lun', 'mie', 'vie'],
207
+ contactDays: ['mar', 'jue']
208
+ })
209
+
210
+ customerAPI.createCustomer(customerData).then((response: any) => {
211
+ customerAPI.validateSuccessResponse(response, 201)
212
+ createdCustomers.push(response.body.data)
213
+
214
+ const customer = response.body.data
215
+ customerAPI.validateCustomerObject(customer)
216
+
217
+ // Verify provided data
218
+ expect(customer.name).to.eq(customerData.name)
219
+ expect(Number(customer.account)).to.eq(customerData.account)
220
+ expect(customer.office).to.eq(customerData.office)
221
+ expect(customer.phone).to.eq(customerData.phone)
222
+ expect(customer.salesRep).to.eq(customerData.salesRep)
223
+ expect(customer.visitDays).to.deep.eq(customerData.visitDays)
224
+ expect(customer.contactDays).to.deep.eq(customerData.contactDays)
225
+
226
+ cy.log(`Created customer: ${customer.name} (ID: ${customer.id})`)
227
+ })
228
+ })
229
+
230
+ it('CUST_API_011: Should create customer with minimal data and default values', () => {
231
+ const minimalData = {
232
+ name: `Minimal Customer ${Date.now()}`,
233
+ account: customerAPI.generateUniqueAccount(),
234
+ office: 'Main Office'
235
+ }
236
+
237
+ customerAPI.createCustomer(minimalData).then((response: any) => {
238
+ customerAPI.validateSuccessResponse(response, 201)
239
+ createdCustomers.push(response.body.data)
240
+
241
+ const customer = response.body.data
242
+ customerAPI.validateCustomerObject(customer)
243
+
244
+ // Verify required fields
245
+ expect(customer.name).to.eq(minimalData.name)
246
+ expect(Number(customer.account)).to.eq(minimalData.account)
247
+ expect(customer.office).to.eq(minimalData.office)
248
+
249
+ // Verify defaults for optional fields
250
+ expect(customer.visitDays).to.deep.eq([])
251
+ expect(customer.contactDays).to.deep.eq([])
252
+
253
+ cy.log(`Created customer with minimal data: ${customer.id}`)
254
+ })
255
+ })
256
+
257
+ it('CUST_API_012: Should create customer with visitDays/contactDays', () => {
258
+ const customerData = customerAPI.generateRandomCustomerData({
259
+ visitDays: ['lun', 'mar', 'mie'],
260
+ contactDays: ['jue', 'vie']
261
+ })
262
+
263
+ customerAPI.createCustomer(customerData).then((response: any) => {
264
+ customerAPI.validateSuccessResponse(response, 201)
265
+ createdCustomers.push(response.body.data)
266
+
267
+ const customer = response.body.data
268
+ expect(customer.visitDays).to.be.an('array')
269
+ expect(customer.visitDays).to.deep.eq(['lun', 'mar', 'mie'])
270
+ expect(customer.contactDays).to.be.an('array')
271
+ expect(customer.contactDays).to.deep.eq(['jue', 'vie'])
272
+
273
+ cy.log(`Created customer with visitDays: ${JSON.stringify(customer.visitDays)}`)
274
+ })
275
+ })
276
+
277
+ it('CUST_API_013: Should reject creation without name', () => {
278
+ const invalidData = {
279
+ account: customerAPI.generateUniqueAccount(),
280
+ office: 'Test Office'
281
+ // Missing: name
282
+ }
283
+
284
+ customerAPI.createCustomer(invalidData).then((response: any) => {
285
+ customerAPI.validateErrorResponse(response, 400, 'VALIDATION_ERROR')
286
+
287
+ cy.log('Creation without name rejected with VALIDATION_ERROR')
288
+ })
289
+ })
290
+
291
+ it('CUST_API_014: Should reject creation without account', () => {
292
+ const invalidData = {
293
+ name: 'Test Customer',
294
+ office: 'Test Office'
295
+ // Missing: account
296
+ }
297
+
298
+ customerAPI.createCustomer(invalidData).then((response: any) => {
299
+ // Can be 400 (validation) or 500 (DB constraint for INTEGER NOT NULL)
300
+ expect(response.status).to.be.oneOf([400, 500])
301
+ expect(response.body).to.have.property('success', false)
302
+
303
+ cy.log('Creation without account rejected')
304
+ })
305
+ })
306
+
307
+ it('CUST_API_015: Should reject creation without office', () => {
308
+ const invalidData = {
309
+ name: 'Test Customer',
310
+ account: customerAPI.generateUniqueAccount()
311
+ // Missing: office
312
+ }
313
+
314
+ customerAPI.createCustomer(invalidData).then((response: any) => {
315
+ customerAPI.validateErrorResponse(response, 400, 'VALIDATION_ERROR')
316
+
317
+ cy.log('Creation without office rejected with VALIDATION_ERROR')
318
+ })
319
+ })
320
+
321
+ it('CUST_API_016: Should reject duplicate account number', () => {
322
+ // First create a customer
323
+ const customerData = customerAPI.generateRandomCustomerData()
324
+
325
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
326
+ expect(createResponse.status).to.eq(201)
327
+ createdCustomers.push(createResponse.body.data)
328
+
329
+ // Try to create another customer with the same account
330
+ const duplicateData = customerAPI.generateRandomCustomerData({
331
+ account: customerData.account // Same account number
332
+ })
333
+
334
+ customerAPI.createCustomer(duplicateData).then((response: any) => {
335
+ // Should fail due to UNIQUE constraint
336
+ expect(response.status).to.be.oneOf([400, 409, 500])
337
+ expect(response.body).to.have.property('success', false)
338
+
339
+ cy.log('Duplicate account number rejected')
340
+ })
341
+ })
342
+ })
343
+
344
+ it('CUST_API_017: Should reject creation without x-team-id', () => {
345
+ const noTeamAPI = new CustomerAPIController(BASE_URL, SUPERADMIN_API_KEY, null)
346
+ const customerData = noTeamAPI.generateRandomCustomerData()
347
+
348
+ noTeamAPI.createCustomer(customerData).then((response: any) => {
349
+ expect(response.status).to.eq(400)
350
+ expect(response.body).to.have.property('success', false)
351
+ expect(response.body).to.have.property('code', 'TEAM_CONTEXT_REQUIRED')
352
+
353
+ cy.log('Creation without x-team-id rejected with TEAM_CONTEXT_REQUIRED')
354
+ })
355
+ })
356
+ })
357
+
358
+ // ============================================
359
+ // GET /api/v1/customers/{id} - Get Customer by ID
360
+ // ============================================
361
+ describe('GET /api/v1/customers/{id} - Get Customer by ID', () => {
362
+ it('CUST_API_020: Should get customer by valid ID', () => {
363
+ // First create a customer
364
+ const customerData = customerAPI.generateRandomCustomerData()
365
+
366
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
367
+ expect(createResponse.status).to.eq(201)
368
+ createdCustomers.push(createResponse.body.data)
369
+
370
+ const customerId = createResponse.body.data.id
371
+
372
+ // Get the customer by ID
373
+ customerAPI.getCustomerById(customerId).then((response: any) => {
374
+ customerAPI.validateSuccessResponse(response, 200)
375
+
376
+ const customer = response.body.data
377
+ customerAPI.validateCustomerObject(customer)
378
+ expect(customer.id).to.eq(customerId)
379
+ expect(customer.name).to.eq(customerData.name)
380
+
381
+ cy.log(`Retrieved customer: ${customer.name}`)
382
+ })
383
+ })
384
+ })
385
+
386
+ it('CUST_API_021: Should return 404 for non-existent customer', () => {
387
+ const fakeId = 'non-existent-customer-id-12345'
388
+
389
+ customerAPI.getCustomerById(fakeId).then((response: any) => {
390
+ expect(response.status).to.eq(404)
391
+ expect(response.body).to.have.property('success', false)
392
+
393
+ cy.log('Non-existent customer returns 404')
394
+ })
395
+ })
396
+
397
+ // Note: No CUST_API_022 test for "access other user's record" because
398
+ // customers entity has shared: true - all team members can see all customers
399
+ })
400
+
401
+ // ============================================
402
+ // PATCH /api/v1/customers/{id} - Update Customer
403
+ // ============================================
404
+ describe('PATCH /api/v1/customers/{id} - Update Customer', () => {
405
+ it('CUST_API_030: Should update customer with valid data', () => {
406
+ // First create a customer
407
+ customerAPI.createTestCustomer().then((testCustomer: any) => {
408
+ createdCustomers.push(testCustomer)
409
+
410
+ const updateData = {
411
+ name: 'Updated Customer Name',
412
+ phone: '+1-555-9999'
413
+ }
414
+
415
+ customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
416
+ customerAPI.validateSuccessResponse(response, 200)
417
+
418
+ const customer = response.body.data
419
+ expect(customer.name).to.eq(updateData.name)
420
+ expect(customer.phone).to.eq(updateData.phone)
421
+ // Original values should be preserved
422
+ expect(customer.office).to.eq(testCustomer.office)
423
+
424
+ cy.log(`Updated customer: ${customer.name}`)
425
+ })
426
+ })
427
+ })
428
+
429
+ it('CUST_API_031: Should update customer office', () => {
430
+ customerAPI.createTestCustomer().then((testCustomer: any) => {
431
+ createdCustomers.push(testCustomer)
432
+
433
+ const newOffice = 'New Branch Office'
434
+
435
+ customerAPI.updateCustomer(testCustomer.id, { office: newOffice }).then((response: any) => {
436
+ customerAPI.validateSuccessResponse(response, 200)
437
+ expect(response.body.data.office).to.eq(newOffice)
438
+
439
+ cy.log(`Updated office to: ${newOffice}`)
440
+ })
441
+ })
442
+ })
443
+
444
+ it('CUST_API_032: Should update customer salesRep', () => {
445
+ customerAPI.createTestCustomer().then((testCustomer: any) => {
446
+ createdCustomers.push(testCustomer)
447
+
448
+ const newSalesRep = 'New Sales Representative'
449
+
450
+ customerAPI
451
+ .updateCustomer(testCustomer.id, { salesRep: newSalesRep })
452
+ .then((response: any) => {
453
+ customerAPI.validateSuccessResponse(response, 200)
454
+ expect(response.body.data.salesRep).to.eq(newSalesRep)
455
+
456
+ cy.log(`Updated salesRep to: ${newSalesRep}`)
457
+ })
458
+ })
459
+ })
460
+
461
+ it('CUST_API_033: Should update customer visitDays/contactDays', () => {
462
+ customerAPI.createTestCustomer().then((testCustomer: any) => {
463
+ createdCustomers.push(testCustomer)
464
+
465
+ const updateData = {
466
+ visitDays: ['lun', 'vie'],
467
+ contactDays: ['mar', 'jue']
468
+ }
469
+
470
+ customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
471
+ customerAPI.validateSuccessResponse(response, 200)
472
+ expect(response.body.data.visitDays).to.deep.eq(updateData.visitDays)
473
+ expect(response.body.data.contactDays).to.deep.eq(updateData.contactDays)
474
+
475
+ cy.log(`Updated visitDays and contactDays`)
476
+ })
477
+ })
478
+ })
479
+
480
+ it('CUST_API_034: Should reject update to duplicate account', () => {
481
+ // Create two customers
482
+ customerAPI.createTestCustomer().then((customer1: any) => {
483
+ createdCustomers.push(customer1)
484
+
485
+ customerAPI.createTestCustomer().then((customer2: any) => {
486
+ createdCustomers.push(customer2)
487
+
488
+ // Try to update customer2's account to customer1's account
489
+ customerAPI
490
+ .updateCustomer(customer2.id, { account: customer1.account })
491
+ .then((response: any) => {
492
+ // Should fail due to UNIQUE constraint
493
+ expect(response.status).to.be.oneOf([400, 409, 500])
494
+ expect(response.body).to.have.property('success', false)
495
+
496
+ cy.log('Update to duplicate account rejected')
497
+ })
498
+ })
499
+ })
500
+ })
501
+
502
+ it('CUST_API_035: Should return 404 for non-existent customer', () => {
503
+ const fakeId = 'non-existent-customer-id-12345'
504
+
505
+ customerAPI.updateCustomer(fakeId, { name: 'New Name' }).then((response: any) => {
506
+ expect(response.status).to.eq(404)
507
+ expect(response.body).to.have.property('success', false)
508
+
509
+ cy.log('Update non-existent customer returns 404')
510
+ })
511
+ })
512
+
513
+ it('CUST_API_036: Should reject empty update body', () => {
514
+ customerAPI.createTestCustomer().then((testCustomer: any) => {
515
+ createdCustomers.push(testCustomer)
516
+
517
+ customerAPI.updateCustomer(testCustomer.id, {}).then((response: any) => {
518
+ expect(response.status).to.eq(400)
519
+ expect(response.body).to.have.property('success', false)
520
+ // Error code can be NO_FIELDS or HTTP_400 depending on validation layer
521
+ expect(response.body.code).to.be.oneOf(['NO_FIELDS', 'HTTP_400'])
522
+
523
+ cy.log('Empty update body rejected')
524
+ })
525
+ })
526
+ })
527
+ })
528
+
529
+ // ============================================
530
+ // DELETE /api/v1/customers/{id} - Delete Customer
531
+ // ============================================
532
+ describe('DELETE /api/v1/customers/{id} - Delete Customer', () => {
533
+ it('CUST_API_040: Should delete customer by valid ID', () => {
534
+ // Create a customer to delete
535
+ const customerData = customerAPI.generateRandomCustomerData()
536
+
537
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
538
+ expect(createResponse.status).to.eq(201)
539
+ const customerId = createResponse.body.data.id
540
+
541
+ // Delete the customer
542
+ customerAPI.deleteCustomer(customerId).then((response: any) => {
543
+ customerAPI.validateSuccessResponse(response, 200)
544
+ expect(response.body.data).to.have.property('success', true)
545
+ expect(response.body.data).to.have.property('id', customerId)
546
+
547
+ cy.log(`Deleted customer: ${customerId}`)
548
+ })
549
+ })
550
+ })
551
+
552
+ it('CUST_API_041: Should return 404 for non-existent customer', () => {
553
+ const fakeId = 'non-existent-customer-id-12345'
554
+
555
+ customerAPI.deleteCustomer(fakeId).then((response: any) => {
556
+ expect(response.status).to.eq(404)
557
+ expect(response.body).to.have.property('success', false)
558
+
559
+ cy.log('Delete non-existent customer returns 404')
560
+ })
561
+ })
562
+
563
+ it('CUST_API_042: Should verify deletion persists', () => {
564
+ // Create a customer
565
+ const customerData = customerAPI.generateRandomCustomerData()
566
+
567
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
568
+ expect(createResponse.status).to.eq(201)
569
+ const customerId = createResponse.body.data.id
570
+
571
+ // Delete it
572
+ customerAPI.deleteCustomer(customerId).then((deleteResponse: any) => {
573
+ expect(deleteResponse.status).to.eq(200)
574
+
575
+ // Verify it's gone
576
+ customerAPI.getCustomerById(customerId).then((getResponse: any) => {
577
+ expect(getResponse.status).to.eq(404)
578
+ expect(getResponse.body).to.have.property('success', false)
579
+
580
+ cy.log('Deletion verified - customer no longer exists')
581
+ })
582
+ })
583
+ })
584
+ })
585
+ })
586
+
587
+ // ============================================
588
+ // Integration - Complete CRUD Lifecycle
589
+ // ============================================
590
+ describe('Integration - Complete CRUD Lifecycle', () => {
591
+ it('CUST_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {
592
+ // 1. CREATE
593
+ const customerData = customerAPI.generateRandomCustomerData({
594
+ phone: '+1-555-0000',
595
+ salesRep: 'Initial Rep',
596
+ visitDays: ['lun'],
597
+ contactDays: ['mar']
598
+ })
599
+
600
+ customerAPI.createCustomer(customerData).then((createResponse: any) => {
601
+ customerAPI.validateSuccessResponse(createResponse, 201)
602
+ const customerId = createResponse.body.data.id
603
+
604
+ cy.log(`1. Created customer: ${customerId}`)
605
+
606
+ // 2. READ
607
+ customerAPI.getCustomerById(customerId).then((readResponse: any) => {
608
+ customerAPI.validateSuccessResponse(readResponse, 200)
609
+ expect(readResponse.body.data.name).to.eq(customerData.name)
610
+
611
+ cy.log(`2. Read customer: ${readResponse.body.data.name}`)
612
+
613
+ // 3. UPDATE
614
+ const updateData = {
615
+ name: 'Updated Lifecycle Customer',
616
+ phone: '+1-555-9999',
617
+ salesRep: 'Updated Rep',
618
+ visitDays: ['lun', 'mie', 'vie']
619
+ }
620
+
621
+ customerAPI.updateCustomer(customerId, updateData).then((updateResponse: any) => {
622
+ customerAPI.validateSuccessResponse(updateResponse, 200)
623
+ expect(updateResponse.body.data.name).to.eq(updateData.name)
624
+ expect(updateResponse.body.data.phone).to.eq(updateData.phone)
625
+
626
+ cy.log(`3. Updated customer: ${updateResponse.body.data.name}`)
627
+
628
+ // 4. DELETE
629
+ customerAPI.deleteCustomer(customerId).then((deleteResponse: any) => {
630
+ customerAPI.validateSuccessResponse(deleteResponse, 200)
631
+ expect(deleteResponse.body.data).to.have.property('success', true)
632
+
633
+ cy.log(`4. Deleted customer: ${customerId}`)
634
+
635
+ // 5. VERIFY DELETION
636
+ customerAPI.getCustomerById(customerId).then((verifyResponse: any) => {
637
+ expect(verifyResponse.status).to.eq(404)
638
+
639
+ cy.log('5. Verified deletion - customer no longer exists')
640
+ cy.log('Full CRUD lifecycle completed successfully!')
641
+ })
642
+ })
643
+ })
644
+ })
645
+ })
646
+ })
647
+ })
648
+ })