@nextsparkjs/theme-crm 0.1.0-beta.18 → 0.1.0-beta.20

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 (70) hide show
  1. package/package.json +2 -2
  2. package/tests/cypress/e2e/api/activities/activities-crud.cy.ts +686 -0
  3. package/tests/cypress/e2e/api/campaigns/campaigns-crud.cy.ts +592 -0
  4. package/tests/cypress/e2e/api/companies/companies-crud.cy.ts +682 -0
  5. package/tests/cypress/e2e/api/contacts/contacts-crud.cy.ts +668 -0
  6. package/tests/cypress/e2e/api/leads/leads-crud.cy.ts +648 -0
  7. package/tests/cypress/e2e/api/notes/notes-crud.cy.ts +424 -0
  8. package/tests/cypress/e2e/api/opportunities/opportunities-crud.cy.ts +865 -0
  9. package/tests/cypress/e2e/api/pipelines/pipelines-crud.cy.ts +545 -0
  10. package/tests/cypress/e2e/api/products/products-crud.cy.ts +447 -0
  11. package/tests/cypress/e2e/ui/activities/activities-admin.cy.ts +268 -0
  12. package/tests/cypress/e2e/ui/activities/activities-member.cy.ts +257 -0
  13. package/tests/cypress/e2e/ui/activities/activities-owner.cy.ts +268 -0
  14. package/tests/cypress/e2e/ui/companies/companies-admin.cy.ts +188 -0
  15. package/tests/cypress/e2e/ui/companies/companies-member.cy.ts +166 -0
  16. package/tests/cypress/e2e/ui/companies/companies-owner.cy.ts +189 -0
  17. package/tests/cypress/e2e/ui/contacts/contacts-admin.cy.ts +252 -0
  18. package/tests/cypress/e2e/ui/contacts/contacts-member.cy.ts +224 -0
  19. package/tests/cypress/e2e/ui/contacts/contacts-owner.cy.ts +236 -0
  20. package/tests/cypress/e2e/ui/leads/leads-admin.cy.ts +286 -0
  21. package/tests/cypress/e2e/ui/leads/leads-member.cy.ts +193 -0
  22. package/tests/cypress/e2e/ui/leads/leads-owner.cy.ts +210 -0
  23. package/tests/cypress/e2e/ui/opportunities/opportunities-admin.cy.ts +197 -0
  24. package/tests/cypress/e2e/ui/opportunities/opportunities-member.cy.ts +229 -0
  25. package/tests/cypress/e2e/ui/opportunities/opportunities-owner.cy.ts +196 -0
  26. package/tests/cypress/e2e/ui/pipelines/pipelines-admin.cy.ts +320 -0
  27. package/tests/cypress/e2e/ui/pipelines/pipelines-member.cy.ts +262 -0
  28. package/tests/cypress/e2e/ui/pipelines/pipelines-owner.cy.ts +282 -0
  29. package/tests/cypress/fixtures/blocks.json +9 -0
  30. package/tests/cypress/fixtures/entities.json +240 -0
  31. package/tests/cypress/src/components/CRMDataTable.js +223 -0
  32. package/tests/cypress/src/components/CRMMobileNav.js +138 -0
  33. package/tests/cypress/src/components/CRMSidebar.js +145 -0
  34. package/tests/cypress/src/components/CRMTopBar.js +194 -0
  35. package/tests/cypress/src/components/DealCard.js +197 -0
  36. package/tests/cypress/src/components/EntityDetail.ts +290 -0
  37. package/tests/cypress/src/components/EntityForm.ts +357 -0
  38. package/tests/cypress/src/components/EntityList.ts +360 -0
  39. package/tests/cypress/src/components/PipelineKanban.js +204 -0
  40. package/tests/cypress/src/components/StageColumn.js +196 -0
  41. package/tests/cypress/src/components/index.js +13 -0
  42. package/tests/cypress/src/components/index.ts +22 -0
  43. package/tests/cypress/src/controllers/ActivityAPIController.ts +113 -0
  44. package/tests/cypress/src/controllers/BaseAPIController.ts +307 -0
  45. package/tests/cypress/src/controllers/CampaignAPIController.ts +114 -0
  46. package/tests/cypress/src/controllers/CompanyAPIController.ts +112 -0
  47. package/tests/cypress/src/controllers/ContactAPIController.ts +104 -0
  48. package/tests/cypress/src/controllers/LeadAPIController.ts +96 -0
  49. package/tests/cypress/src/controllers/NoteAPIController.ts +130 -0
  50. package/tests/cypress/src/controllers/OpportunityAPIController.ts +134 -0
  51. package/tests/cypress/src/controllers/PipelineAPIController.ts +116 -0
  52. package/tests/cypress/src/controllers/ProductAPIController.ts +113 -0
  53. package/tests/cypress/src/controllers/index.ts +35 -0
  54. package/tests/cypress/src/entities/ActivitiesPOM.ts +130 -0
  55. package/tests/cypress/src/entities/CompaniesPOM.ts +117 -0
  56. package/tests/cypress/src/entities/ContactsPOM.ts +117 -0
  57. package/tests/cypress/src/entities/LeadsPOM.ts +129 -0
  58. package/tests/cypress/src/entities/OpportunitiesPOM.ts +178 -0
  59. package/tests/cypress/src/entities/PipelinesPOM.ts +341 -0
  60. package/tests/cypress/src/entities/index.ts +31 -0
  61. package/tests/cypress/src/forms/OpportunityForm.js +316 -0
  62. package/tests/cypress/src/forms/PipelineForm.js +243 -0
  63. package/tests/cypress/src/forms/index.js +8 -0
  64. package/tests/cypress/src/index.js +22 -0
  65. package/tests/cypress/src/index.ts +68 -0
  66. package/tests/cypress/src/selectors.ts +50 -0
  67. package/tests/cypress/src/session-helpers.ts +94 -0
  68. package/tests/cypress/support/e2e.ts +89 -0
  69. package/tests/cypress.config.ts +165 -0
  70. package/tests/tsconfig.json +15 -0
@@ -0,0 +1,682 @@
1
+ /**
2
+ * Companies API - CRUD Tests
3
+ *
4
+ * Comprehensive test suite for Company API endpoints.
5
+ * Tests GET, POST, PATCH, DELETE operations.
6
+ *
7
+ * Entity characteristics:
8
+ * - Required fields: name
9
+ * - Optional fields: website, industry, size, type, phone, address, city, country, description
10
+ * - Access: shared within team (all team members see all companies)
11
+ * - Team context: required (x-team-id header)
12
+ * - Special: Has related contacts
13
+ */
14
+
15
+ /// <reference types="cypress" />
16
+
17
+ import { CompanyAPIController } from '../../../src/controllers'
18
+
19
+ describe('Companies API - CRUD Operations', () => {
20
+ // Test constants
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
+ // Controller instance
26
+ let companyAPI: InstanceType<typeof CompanyAPIController>
27
+
28
+ // Track created companies for cleanup
29
+ let createdCompanies: any[] = []
30
+
31
+ before(() => {
32
+ // Initialize controller with superadmin credentials
33
+ companyAPI = new CompanyAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
34
+ })
35
+
36
+ afterEach(() => {
37
+ // Cleanup created companies after each test
38
+ createdCompanies.forEach((company) => {
39
+ if (company?.id) {
40
+ companyAPI.delete(company.id)
41
+ }
42
+ })
43
+ createdCompanies = []
44
+ // Small delay to allow database connections to be released
45
+ cy.wait(200)
46
+ })
47
+
48
+ // ============================================
49
+ // GET /api/v1/companies - List Companies
50
+ // ============================================
51
+ describe('GET /api/v1/companies - List Companies', () => {
52
+ it('COMP_API_001: Should list companies with valid API key', () => {
53
+ companyAPI.getAll().then((response: any) => {
54
+ companyAPI.validateSuccessResponse(response, 200)
55
+ expect(response.body.data).to.be.an('array')
56
+ expect(response.body.info).to.have.property('page')
57
+ expect(response.body.info).to.have.property('limit')
58
+ expect(response.body.info).to.have.property('total')
59
+ expect(response.body.info).to.have.property('totalPages')
60
+
61
+ cy.log(`Found ${response.body.data.length} companies`)
62
+ })
63
+ })
64
+
65
+ it('COMP_API_002: Should list companies with pagination', () => {
66
+ companyAPI.getAll({ page: 1, limit: 5 }).then((response: any) => {
67
+ companyAPI.validateSuccessResponse(response, 200)
68
+ expect(response.body.info.page).to.eq(1)
69
+ expect(response.body.info.limit).to.eq(5)
70
+ expect(response.body.data.length).to.be.at.most(5)
71
+
72
+ cy.log(`Page 1 with limit 5: ${response.body.data.length} companies`)
73
+ })
74
+ })
75
+
76
+ it('COMP_API_003: Should filter companies by industry', () => {
77
+ // First create a company with a specific industry
78
+ const testIndustry = 'Technology'
79
+ const companyData = companyAPI.generateRandomData({ industry: testIndustry })
80
+
81
+ companyAPI.create(companyData).then((createResponse: any) => {
82
+ expect(createResponse.status).to.eq(201)
83
+ createdCompanies.push(createResponse.body.data)
84
+
85
+ // Now filter by that industry
86
+ companyAPI.getAll({ industry: testIndustry }).then((response: any) => {
87
+ companyAPI.validateSuccessResponse(response, 200)
88
+ expect(response.body.data).to.be.an('array')
89
+
90
+ // All returned companies should have the specified industry
91
+ response.body.data.forEach((company: any) => {
92
+ expect(company.industry).to.eq(testIndustry)
93
+ })
94
+
95
+ cy.log(`Found ${response.body.data.length} companies with industry '${testIndustry}'`)
96
+ })
97
+ })
98
+ })
99
+
100
+ it('COMP_API_004: Should filter companies by size', () => {
101
+ // First create a company with a specific size
102
+ const testSize = '51-200'
103
+ const companyData = companyAPI.generateRandomData({ size: testSize })
104
+
105
+ companyAPI.create(companyData).then((createResponse: any) => {
106
+ expect(createResponse.status).to.eq(201)
107
+ createdCompanies.push(createResponse.body.data)
108
+
109
+ // Now filter by that size
110
+ companyAPI.getAll({ size: testSize }).then((response: any) => {
111
+ companyAPI.validateSuccessResponse(response, 200)
112
+ expect(response.body.data).to.be.an('array')
113
+
114
+ // All returned companies should have the specified size
115
+ response.body.data.forEach((company: any) => {
116
+ expect(company.size).to.eq(testSize)
117
+ })
118
+
119
+ cy.log(`Found ${response.body.data.length} companies with size '${testSize}'`)
120
+ })
121
+ })
122
+ })
123
+
124
+ it('COMP_API_005: Should search companies by name', () => {
125
+ // Create a company with a unique searchable term
126
+ const uniqueTerm = `SearchCompany${Date.now()}`
127
+ const companyData = companyAPI.generateRandomData({
128
+ name: `${uniqueTerm} Corporation`
129
+ })
130
+
131
+ companyAPI.create(companyData).then((createResponse: any) => {
132
+ expect(createResponse.status).to.eq(201)
133
+ createdCompanies.push(createResponse.body.data)
134
+
135
+ // Search for the unique term
136
+ companyAPI.getAll({ search: uniqueTerm }).then((response: any) => {
137
+ companyAPI.validateSuccessResponse(response, 200)
138
+ expect(response.body.data).to.be.an('array')
139
+ expect(response.body.data.length).to.be.greaterThan(0)
140
+
141
+ // Verify the found company contains our search term
142
+ const foundCompany = response.body.data.find(
143
+ (c: any) => c.id === createResponse.body.data.id
144
+ )
145
+ expect(foundCompany).to.exist
146
+ expect(foundCompany.name).to.include(uniqueTerm)
147
+
148
+ cy.log(`Search found ${response.body.data.length} companies matching '${uniqueTerm}'`)
149
+ })
150
+ })
151
+ })
152
+
153
+ it('COMP_API_006: Should handle search with non-matching term', () => {
154
+ const nonExistentTerm = 'xyzzy1234567890abcdef'
155
+
156
+ companyAPI.getAll({ search: nonExistentTerm }).then((response: any) => {
157
+ companyAPI.validateSuccessResponse(response, 200)
158
+ expect(response.body.data).to.be.an('array')
159
+
160
+ // If search filtering works, should be empty or have no matches
161
+ // If search is ignored, will return paginated results
162
+ response.body.data.forEach((company: any) => {
163
+ // Each returned company should not contain the search term in name
164
+ expect(company.name.toLowerCase()).to.not.include(nonExistentTerm.toLowerCase())
165
+ })
166
+
167
+ cy.log(`Search returned ${response.body.data.length} companies (none matching '${nonExistentTerm}')`)
168
+ })
169
+ })
170
+
171
+ it('COMP_API_007: Should reject request without API key', () => {
172
+ const noAuthAPI = new CompanyAPIController(BASE_URL, null, TEAM_ID)
173
+
174
+ noAuthAPI.getAll().then((response: any) => {
175
+ expect(response.status).to.eq(401)
176
+ expect(response.body).to.have.property('success', false)
177
+
178
+ cy.log('Request without API key rejected with 401')
179
+ })
180
+ })
181
+
182
+ it('COMP_API_008: Should reject request without x-team-id', () => {
183
+ const noTeamAPI = new CompanyAPIController(BASE_URL, SUPERADMIN_API_KEY, null)
184
+
185
+ noTeamAPI.getAll().then((response: any) => {
186
+ expect(response.status).to.eq(400)
187
+ expect(response.body).to.have.property('success', false)
188
+ expect(response.body).to.have.property('code', 'TEAM_CONTEXT_REQUIRED')
189
+
190
+ cy.log('Request without x-team-id rejected with TEAM_CONTEXT_REQUIRED')
191
+ })
192
+ })
193
+ })
194
+
195
+ // ============================================
196
+ // POST /api/v1/companies - Create Company
197
+ // ============================================
198
+ describe('POST /api/v1/companies - Create Company', () => {
199
+ it('COMP_API_010: Should create company with valid data', () => {
200
+ const companyData = companyAPI.generateRandomData({
201
+ website: 'https://www.testcompany.com',
202
+ phone: '+1-555-1234',
203
+ address: '123 Tech Blvd',
204
+ city: 'San Francisco',
205
+ country: 'USA'
206
+ })
207
+
208
+ companyAPI.create(companyData).then((response: any) => {
209
+ companyAPI.validateSuccessResponse(response, 201)
210
+ createdCompanies.push(response.body.data)
211
+
212
+ const company = response.body.data
213
+ companyAPI.validateObject(company)
214
+
215
+ // Verify provided data
216
+ expect(company.name).to.eq(companyData.name)
217
+ expect(company.website).to.eq(companyData.website)
218
+ expect(company.industry).to.eq(companyData.industry)
219
+ expect(company.size).to.eq(companyData.size)
220
+ expect(company.type).to.eq(companyData.type)
221
+ expect(company.phone).to.eq(companyData.phone)
222
+ expect(company.address).to.eq(companyData.address)
223
+ expect(company.city).to.eq(companyData.city)
224
+ expect(company.country).to.eq(companyData.country)
225
+
226
+ cy.log(`Created company: ${company.name} (ID: ${company.id})`)
227
+ })
228
+ })
229
+
230
+ it('COMP_API_011: Should create company with minimal data (name only)', () => {
231
+ const minimalData = {
232
+ name: `Minimal Company ${Date.now()}`
233
+ }
234
+
235
+ companyAPI.create(minimalData).then((response: any) => {
236
+ companyAPI.validateSuccessResponse(response, 201)
237
+ createdCompanies.push(response.body.data)
238
+
239
+ const company = response.body.data
240
+ companyAPI.validateObject(company)
241
+
242
+ // Verify required fields
243
+ expect(company.name).to.eq(minimalData.name)
244
+
245
+ // Verify optional fields are null or undefined
246
+ expect(company.website).to.satisfy((val: any) => val === null || val === undefined)
247
+
248
+ cy.log(`Created company with minimal data: ${company.id}`)
249
+ })
250
+ })
251
+
252
+ it('COMP_API_012: Should create company with all optional fields', () => {
253
+ const companyData = companyAPI.generateRandomData({
254
+ name: `Complete Company ${Date.now()}`,
255
+ legalName: 'Complete Company LLC',
256
+ website: 'https://www.complete-company.com',
257
+ email: 'contact@complete-company.com',
258
+ industry: 'Technology',
259
+ size: '201-500',
260
+ type: 'customer',
261
+ phone: '+1-555-9876',
262
+ address: '789 Complete Ave',
263
+ city: 'New York',
264
+ state: 'NY',
265
+ country: 'USA',
266
+ postalCode: '10001',
267
+ rating: 'hot'
268
+ })
269
+
270
+ companyAPI.create(companyData).then((response: any) => {
271
+ companyAPI.validateSuccessResponse(response, 201)
272
+ createdCompanies.push(response.body.data)
273
+
274
+ const company = response.body.data
275
+
276
+ // Verify all fields
277
+ expect(company.name).to.eq(companyData.name)
278
+ expect(company.legalName).to.eq(companyData.legalName)
279
+ expect(company.website).to.eq(companyData.website)
280
+ expect(company.email).to.eq(companyData.email)
281
+ expect(company.industry).to.eq(companyData.industry)
282
+ expect(company.size).to.eq(companyData.size)
283
+ expect(company.type).to.eq(companyData.type)
284
+ expect(company.phone).to.eq(companyData.phone)
285
+ expect(company.address).to.eq(companyData.address)
286
+ expect(company.city).to.eq(companyData.city)
287
+ expect(company.country).to.eq(companyData.country)
288
+
289
+ cy.log(`Created company with all fields: ${company.id}`)
290
+ })
291
+ })
292
+
293
+ it('COMP_API_013: Should reject creation without name', () => {
294
+ const invalidData = {
295
+ website: 'https://www.invalid.com',
296
+ industry: 'Technology'
297
+ // Missing: name
298
+ }
299
+
300
+ companyAPI.create(invalidData).then((response: any) => {
301
+ companyAPI.validateErrorResponse(response, 400, 'VALIDATION_ERROR')
302
+
303
+ cy.log('Creation without name rejected with VALIDATION_ERROR')
304
+ })
305
+ })
306
+
307
+ it('COMP_API_014: Should create company with website (URL validation)', () => {
308
+ // Use minimal data to avoid other field validation issues
309
+ const companyData = {
310
+ name: `Website Company ${Date.now()}`,
311
+ website: 'https://www.valid-website.com'
312
+ }
313
+
314
+ companyAPI.create(companyData).then((response: any) => {
315
+ companyAPI.validateSuccessResponse(response, 201)
316
+ createdCompanies.push(response.body.data)
317
+
318
+ const company = response.body.data
319
+ expect(company.website).to.eq(companyData.website)
320
+
321
+ cy.log(`Created company with valid website: ${company.website}`)
322
+ })
323
+ })
324
+
325
+ it('COMP_API_015: Should handle creation with non-standard website format', () => {
326
+ // Note: The API may accept any string for website and may auto-prefix https://
327
+ const testData = {
328
+ name: `Website Test Company ${Date.now()}`,
329
+ website: 'not-a-valid-url'
330
+ }
331
+
332
+ companyAPI.create(testData).then((response: any) => {
333
+ // API may accept the value or reject it - handle both cases
334
+ if (response.status === 201) {
335
+ // If accepted, clean up
336
+ createdCompanies.push(response.body.data)
337
+ // The API might auto-prefix with https://
338
+ expect(response.body.data.website).to.include('not-a-valid-url')
339
+ cy.log('API accepted non-standard website format')
340
+ } else {
341
+ // If rejected, verify error response
342
+ expect(response.status).to.be.oneOf([400, 422])
343
+ expect(response.body).to.have.property('success', false)
344
+ cy.log('Creation with invalid website format rejected')
345
+ }
346
+ })
347
+ })
348
+
349
+ it('COMP_API_016: Should create company with industry and size', () => {
350
+ const companyData = companyAPI.generateRandomData({
351
+ name: `Industry Company ${Date.now()}`,
352
+ industry: 'Healthcare',
353
+ size: '500+'
354
+ })
355
+
356
+ companyAPI.create(companyData).then((response: any) => {
357
+ companyAPI.validateSuccessResponse(response, 201)
358
+ createdCompanies.push(response.body.data)
359
+
360
+ const company = response.body.data
361
+ expect(company.industry).to.eq(companyData.industry)
362
+ expect(company.size).to.eq(companyData.size)
363
+
364
+ cy.log(`Created company with industry: ${company.industry}, size: ${company.size}`)
365
+ })
366
+ })
367
+
368
+ it('COMP_API_017: Should reject creation without x-team-id', () => {
369
+ const noTeamAPI = new CompanyAPIController(BASE_URL, SUPERADMIN_API_KEY, null)
370
+ const companyData = noTeamAPI.generateRandomData()
371
+
372
+ noTeamAPI.create(companyData).then((response: any) => {
373
+ expect(response.status).to.eq(400)
374
+ expect(response.body).to.have.property('success', false)
375
+ expect(response.body).to.have.property('code', 'TEAM_CONTEXT_REQUIRED')
376
+
377
+ cy.log('Creation without x-team-id rejected with TEAM_CONTEXT_REQUIRED')
378
+ })
379
+ })
380
+ })
381
+
382
+ // ============================================
383
+ // GET /api/v1/companies/{id} - Get Company by ID
384
+ // ============================================
385
+ describe('GET /api/v1/companies/{id} - Get Company by ID', () => {
386
+ it('COMP_API_020: Should get company by valid ID', () => {
387
+ // First create a company
388
+ const companyData = companyAPI.generateRandomData()
389
+
390
+ companyAPI.create(companyData).then((createResponse: any) => {
391
+ expect(createResponse.status).to.eq(201)
392
+ createdCompanies.push(createResponse.body.data)
393
+
394
+ const companyId = createResponse.body.data.id
395
+
396
+ // Get the company by ID
397
+ companyAPI.getById(companyId).then((response: any) => {
398
+ companyAPI.validateSuccessResponse(response, 200)
399
+
400
+ const company = response.body.data
401
+ companyAPI.validateObject(company)
402
+ expect(company.id).to.eq(companyId)
403
+ expect(company.name).to.eq(companyData.name)
404
+
405
+ cy.log(`Retrieved company: ${company.name}`)
406
+ })
407
+ })
408
+ })
409
+
410
+ it('COMP_API_021: Should return 404 for non-existent company', () => {
411
+ const fakeId = 'non-existent-company-id-12345'
412
+
413
+ companyAPI.getById(fakeId).then((response: any) => {
414
+ expect(response.status).to.eq(404)
415
+ expect(response.body).to.have.property('success', false)
416
+
417
+ cy.log('Non-existent company returns 404')
418
+ })
419
+ })
420
+
421
+ // Note: No COMP_API_022 test for "access other user's record" because
422
+ // companies entity has shared: true - all team members can see all companies
423
+ })
424
+
425
+ // ============================================
426
+ // PATCH /api/v1/companies/{id} - Update Company
427
+ // ============================================
428
+ describe('PATCH /api/v1/companies/{id} - Update Company', () => {
429
+ it('COMP_API_030: Should update company with multiple fields', () => {
430
+ // First create a company
431
+ companyAPI.createTestRecord({}, { withRetry: true }).then((testCompany: any) => {
432
+ createdCompanies.push(testCompany)
433
+
434
+ const updateData = {
435
+ name: 'Updated Company Name',
436
+ website: 'https://www.updated-company.com',
437
+ phone: '+1-555-9999',
438
+ city: 'Boston'
439
+ }
440
+
441
+ companyAPI.update(testCompany.id, updateData).then((response: any) => {
442
+ companyAPI.validateSuccessResponse(response, 200)
443
+
444
+ const company = response.body.data
445
+ expect(company.name).to.eq(updateData.name)
446
+ expect(company.website).to.eq(updateData.website)
447
+ expect(company.phone).to.eq(updateData.phone)
448
+ expect(company.city).to.eq(updateData.city)
449
+ // Original values should be preserved
450
+ expect(company.industry).to.eq(testCompany.industry)
451
+
452
+ cy.log(`Updated company: ${company.name}`)
453
+ })
454
+ })
455
+ })
456
+
457
+ it('COMP_API_031: Should update company name', () => {
458
+ companyAPI.createTestRecord({}, { withRetry: true }).then((testCompany: any) => {
459
+ createdCompanies.push(testCompany)
460
+
461
+ const newName = 'New Company Name Inc.'
462
+
463
+ companyAPI.update(testCompany.id, { name: newName }).then((response: any) => {
464
+ companyAPI.validateSuccessResponse(response, 200)
465
+ expect(response.body.data.name).to.eq(newName)
466
+
467
+ cy.log(`Updated name to: ${newName}`)
468
+ })
469
+ })
470
+ })
471
+
472
+ it('COMP_API_032: Should update company industry', () => {
473
+ companyAPI.createTestRecord({}, { withRetry: true }).then((testCompany: any) => {
474
+ createdCompanies.push(testCompany)
475
+
476
+ const newIndustry = 'Finance'
477
+
478
+ companyAPI.update(testCompany.id, { industry: newIndustry }).then((response: any) => {
479
+ companyAPI.validateSuccessResponse(response, 200)
480
+ expect(response.body.data.industry).to.eq(newIndustry)
481
+
482
+ cy.log(`Updated industry to: ${newIndustry}`)
483
+ })
484
+ })
485
+ })
486
+
487
+ it('COMP_API_033: Should update company size and type', () => {
488
+ companyAPI.createTestRecord({}, { withRetry: true }).then((testCompany: any) => {
489
+ createdCompanies.push(testCompany)
490
+
491
+ const updateData = {
492
+ size: '500+',
493
+ type: 'partner'
494
+ }
495
+
496
+ companyAPI.update(testCompany.id, updateData).then((response: any) => {
497
+ companyAPI.validateSuccessResponse(response, 200)
498
+ expect(response.body.data.size).to.eq(updateData.size)
499
+ expect(response.body.data.type).to.eq(updateData.type)
500
+
501
+ cy.log(`Updated size: ${updateData.size}, type: ${updateData.type}`)
502
+ })
503
+ })
504
+ })
505
+
506
+ it('COMP_API_034: Should update company rating', () => {
507
+ companyAPI.createTestRecord({}, { withRetry: true }).then((testCompany: any) => {
508
+ createdCompanies.push(testCompany)
509
+
510
+ const newRating = 'hot'
511
+
512
+ companyAPI
513
+ .update(testCompany.id, { rating: newRating })
514
+ .then((response: any) => {
515
+ companyAPI.validateSuccessResponse(response, 200)
516
+ expect(response.body.data.rating).to.eq(newRating)
517
+
518
+ cy.log(`Updated rating to: ${newRating}`)
519
+ })
520
+ })
521
+ })
522
+
523
+ it('COMP_API_035: Should return 404 for non-existent company', () => {
524
+ const fakeId = 'non-existent-company-id-12345'
525
+
526
+ companyAPI.update(fakeId, { name: 'New Name' }).then((response: any) => {
527
+ expect(response.status).to.eq(404)
528
+ expect(response.body).to.have.property('success', false)
529
+
530
+ cy.log('Update non-existent company returns 404')
531
+ })
532
+ })
533
+
534
+ it('COMP_API_036: Should reject empty update body', () => {
535
+ companyAPI.createTestRecord({}, { withRetry: true }).then((testCompany: any) => {
536
+ createdCompanies.push(testCompany)
537
+
538
+ companyAPI.update(testCompany.id, {}).then((response: any) => {
539
+ expect(response.status).to.eq(400)
540
+ expect(response.body).to.have.property('success', false)
541
+ // Error code can be NO_FIELDS or HTTP_400 depending on validation layer
542
+ expect(response.body.code).to.be.oneOf(['NO_FIELDS', 'HTTP_400'])
543
+
544
+ cy.log('Empty update body rejected')
545
+ })
546
+ })
547
+ })
548
+ })
549
+
550
+ // ============================================
551
+ // DELETE /api/v1/companies/{id} - Delete Company
552
+ // ============================================
553
+ describe('DELETE /api/v1/companies/{id} - Delete Company', () => {
554
+ it('COMP_API_040: Should delete company by valid ID', () => {
555
+ // Create a company to delete
556
+ const companyData = companyAPI.generateRandomData()
557
+
558
+ companyAPI.create(companyData).then((createResponse: any) => {
559
+ expect(createResponse.status).to.eq(201)
560
+ const companyId = createResponse.body.data.id
561
+
562
+ // Delete the company
563
+ companyAPI.delete(companyId).then((response: any) => {
564
+ companyAPI.validateSuccessResponse(response, 200)
565
+ expect(response.body.data).to.have.property('success', true)
566
+ expect(response.body.data).to.have.property('id', companyId)
567
+
568
+ cy.log(`Deleted company: ${companyId}`)
569
+ })
570
+ })
571
+ })
572
+
573
+ it('COMP_API_041: Should return 404 for non-existent company', () => {
574
+ const fakeId = 'non-existent-company-id-12345'
575
+
576
+ companyAPI.delete(fakeId).then((response: any) => {
577
+ expect(response.status).to.eq(404)
578
+ expect(response.body).to.have.property('success', false)
579
+
580
+ cy.log('Delete non-existent company returns 404')
581
+ })
582
+ })
583
+
584
+ it('COMP_API_042: Should verify deletion persists', () => {
585
+ // Create a company
586
+ const companyData = companyAPI.generateRandomData()
587
+
588
+ companyAPI.create(companyData).then((createResponse: any) => {
589
+ expect(createResponse.status).to.eq(201)
590
+ const companyId = createResponse.body.data.id
591
+
592
+ // Delete it
593
+ companyAPI.delete(companyId).then((deleteResponse: any) => {
594
+ expect(deleteResponse.status).to.eq(200)
595
+
596
+ // Verify it's gone
597
+ companyAPI.getById(companyId).then((getResponse: any) => {
598
+ expect(getResponse.status).to.eq(404)
599
+ expect(getResponse.body).to.have.property('success', false)
600
+
601
+ cy.log('Deletion verified - company no longer exists')
602
+ })
603
+ })
604
+ })
605
+ })
606
+ })
607
+
608
+ // ============================================
609
+ // Integration - Complete CRUD Lifecycle
610
+ // ============================================
611
+ describe('Integration - Complete CRUD Lifecycle', () => {
612
+ it('COMP_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {
613
+ // 1. CREATE
614
+ const companyData = companyAPI.generateRandomData({
615
+ name: 'Lifecycle Test Company',
616
+ website: 'https://www.lifecycle-test.com',
617
+ industry: 'Technology',
618
+ size: '51-200',
619
+ type: 'prospect',
620
+ phone: '+1-555-0000',
621
+ address: '100 Test Street',
622
+ city: 'San Francisco',
623
+ country: 'USA',
624
+ rating: 'warm'
625
+ })
626
+
627
+ companyAPI.create(companyData).then((createResponse: any) => {
628
+ companyAPI.validateSuccessResponse(createResponse, 201)
629
+ const companyId = createResponse.body.data.id
630
+
631
+ cy.log(`1. Created company: ${companyId}`)
632
+
633
+ // 2. READ
634
+ companyAPI.getById(companyId).then((readResponse: any) => {
635
+ companyAPI.validateSuccessResponse(readResponse, 200)
636
+ expect(readResponse.body.data.name).to.eq(companyData.name)
637
+ expect(readResponse.body.data.website).to.eq(companyData.website)
638
+
639
+ cy.log(`2. Read company: ${readResponse.body.data.name}`)
640
+
641
+ // 3. UPDATE
642
+ const updateData = {
643
+ name: 'Updated Lifecycle Company',
644
+ website: 'https://www.updated-lifecycle.com',
645
+ industry: 'Finance',
646
+ size: '201-500',
647
+ type: 'customer',
648
+ phone: '+1-555-9999',
649
+ rating: 'hot'
650
+ }
651
+
652
+ companyAPI.update(companyId, updateData).then((updateResponse: any) => {
653
+ companyAPI.validateSuccessResponse(updateResponse, 200)
654
+ expect(updateResponse.body.data.name).to.eq(updateData.name)
655
+ expect(updateResponse.body.data.website).to.eq(updateData.website)
656
+ expect(updateResponse.body.data.industry).to.eq(updateData.industry)
657
+ expect(updateResponse.body.data.size).to.eq(updateData.size)
658
+ expect(updateResponse.body.data.type).to.eq(updateData.type)
659
+
660
+ cy.log(`3. Updated company: ${updateResponse.body.data.name}`)
661
+
662
+ // 4. DELETE
663
+ companyAPI.delete(companyId).then((deleteResponse: any) => {
664
+ companyAPI.validateSuccessResponse(deleteResponse, 200)
665
+ expect(deleteResponse.body.data).to.have.property('success', true)
666
+
667
+ cy.log(`4. Deleted company: ${companyId}`)
668
+
669
+ // 5. VERIFY DELETION
670
+ companyAPI.getById(companyId).then((verifyResponse: any) => {
671
+ expect(verifyResponse.status).to.eq(404)
672
+
673
+ cy.log('5. Verified deletion - company no longer exists')
674
+ cy.log('Full CRUD lifecycle completed successfully!')
675
+ })
676
+ })
677
+ })
678
+ })
679
+ })
680
+ })
681
+ })
682
+ })