@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,424 @@
1
+ /**
2
+ * Notes API - CRUD Tests
3
+ *
4
+ * Comprehensive test suite for Note API endpoints.
5
+ * Tests GET, POST, PATCH, DELETE operations.
6
+ *
7
+ * Entity characteristics:
8
+ * - Required fields: content
9
+ * - Optional fields: contactId, companyId, opportunityId
10
+ * - Access: shared within team (all team members see all notes)
11
+ * - Team context: required (x-team-id header)
12
+ * - Type: Simple entity (typically lower test count)
13
+ */
14
+
15
+ /// <reference types="cypress" />
16
+
17
+ import { NoteAPIController } from '../../../src/controllers'
18
+
19
+ describe('Notes 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 noteAPI: InstanceType<typeof NoteAPIController>
27
+
28
+ // Track created notes for cleanup
29
+ let createdNotes: any[] = []
30
+
31
+ before(() => {
32
+ // Initialize controller with superadmin credentials
33
+ noteAPI = new NoteAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
34
+ })
35
+
36
+ afterEach(() => {
37
+ // Cleanup created notes after each test
38
+ createdNotes.forEach((note) => {
39
+ if (note?.id) {
40
+ noteAPI.delete(note.id)
41
+ }
42
+ })
43
+ createdNotes = []
44
+ // Small delay to allow database connections to be released
45
+ cy.wait(200)
46
+ })
47
+
48
+ // ============================================
49
+ // GET /api/v1/notes - List Notes
50
+ // ============================================
51
+ describe('GET /api/v1/notes - List Notes', () => {
52
+ it('NOTE_API_001: Should list notes with valid API key', () => {
53
+ noteAPI.getAll().then((response: any) => {
54
+ noteAPI.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} notes`)
62
+ })
63
+ })
64
+
65
+ it('NOTE_API_002: Should list notes with pagination', () => {
66
+ noteAPI.getAll({ page: 1, limit: 5 }).then((response: any) => {
67
+ noteAPI.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} notes`)
73
+ })
74
+ })
75
+
76
+ it('NOTE_API_003: Should filter notes by type', () => {
77
+ // Create a note with a specific type
78
+ const noteData = noteAPI.generateRandomData({ type: 'call' })
79
+
80
+ noteAPI.create(noteData).then((createResponse: any) => {
81
+ expect(createResponse.status).to.eq(201)
82
+ createdNotes.push(createResponse.body.data)
83
+
84
+ // Now filter by that type
85
+ noteAPI.getAll({ type: 'call' }).then((response: any) => {
86
+ noteAPI.validateSuccessResponse(response, 200)
87
+ expect(response.body.data).to.be.an('array')
88
+
89
+ // All returned notes should have the specified type
90
+ response.body.data.forEach((note: any) => {
91
+ expect(note.type).to.eq('call')
92
+ })
93
+
94
+ cy.log(`Found ${response.body.data.length} notes with type 'call'`)
95
+ })
96
+ })
97
+ })
98
+
99
+ it('NOTE_API_004: Should filter notes by isPinned', () => {
100
+ // Create a pinned note
101
+ const noteData = noteAPI.generateRandomData({ isPinned: true })
102
+
103
+ noteAPI.create(noteData).then((createResponse: any) => {
104
+ expect(createResponse.status).to.eq(201)
105
+ createdNotes.push(createResponse.body.data)
106
+
107
+ // Now filter by isPinned
108
+ noteAPI.getAll({ isPinned: true }).then((response: any) => {
109
+ noteAPI.validateSuccessResponse(response, 200)
110
+ expect(response.body.data).to.be.an('array')
111
+
112
+ // All returned notes should be pinned
113
+ response.body.data.forEach((note: any) => {
114
+ expect(note.isPinned).to.eq(true)
115
+ })
116
+
117
+ cy.log(`Found ${response.body.data.length} pinned notes`)
118
+ })
119
+ })
120
+ })
121
+ })
122
+
123
+ // ============================================
124
+ // POST /api/v1/notes - Create Note
125
+ // ============================================
126
+ describe('POST /api/v1/notes - Create Note', () => {
127
+ it('NOTE_API_010: Should create note with valid data', () => {
128
+ const noteData = noteAPI.generateRandomData({
129
+ content: 'Great meeting today. Customer is very interested in our Enterprise plan.',
130
+ type: 'meeting',
131
+ isPinned: true
132
+ })
133
+
134
+ noteAPI.create(noteData).then((response: any) => {
135
+ noteAPI.validateSuccessResponse(response, 201)
136
+ createdNotes.push(response.body.data)
137
+
138
+ const note = response.body.data
139
+ noteAPI.validateObject(note)
140
+
141
+ // Verify provided data
142
+ expect(note.content).to.eq(noteData.content)
143
+ expect(note.type).to.eq(noteData.type)
144
+ expect(note.isPinned).to.eq(noteData.isPinned)
145
+
146
+ cy.log(`Created note: ${note.id}`)
147
+ })
148
+ })
149
+
150
+ it('NOTE_API_011: Should create note with minimal data (content only)', () => {
151
+ const minimalData = {
152
+ content: `Minimal note created at ${new Date().toISOString()}`
153
+ }
154
+
155
+ noteAPI.create(minimalData).then((response: any) => {
156
+ noteAPI.validateSuccessResponse(response, 201)
157
+ createdNotes.push(response.body.data)
158
+
159
+ const note = response.body.data
160
+ noteAPI.validateObject(note)
161
+
162
+ // Verify required fields
163
+ expect(note.content).to.eq(minimalData.content)
164
+
165
+ // Verify optional fields are null or undefined or default
166
+ expect(note.type).to.satisfy((val: any) => val === null || val === undefined || typeof val === 'string')
167
+ expect(note.isPinned).to.satisfy((val: any) => val === null || val === undefined || typeof val === 'boolean')
168
+
169
+ cy.log(`Created note with minimal data: ${note.id}`)
170
+ })
171
+ })
172
+
173
+ it('NOTE_API_012: Should create note with all optional fields', () => {
174
+ // Note: entityType/entityId fields may cause issues without valid references
175
+ noteAPI.createTestRecord({
176
+ title: 'Complete Note Title',
177
+ content: 'Complete note with all fields populated',
178
+ type: 'feedback',
179
+ isPinned: true,
180
+ isPrivate: false
181
+ }, { withRetry: true }).then((note: any) => {
182
+ createdNotes.push(note)
183
+
184
+ // Verify all fields
185
+ expect(note.content).to.eq('Complete note with all fields populated')
186
+ expect(note.title).to.eq('Complete Note Title')
187
+ expect(note.type).to.eq('feedback')
188
+ expect(note.isPinned).to.eq(true)
189
+
190
+ cy.log(`Created note with all fields: ${note.id}`)
191
+ })
192
+ })
193
+
194
+ it('NOTE_API_013: Should reject creation without content', () => {
195
+ const invalidData = {
196
+ type: 'general'
197
+ // Missing: content
198
+ }
199
+
200
+ noteAPI.create(invalidData).then((response: any) => {
201
+ noteAPI.validateErrorResponse(response, 400, 'VALIDATION_ERROR')
202
+
203
+ cy.log('Creation without content rejected with VALIDATION_ERROR')
204
+ })
205
+ })
206
+ })
207
+
208
+ // ============================================
209
+ // GET /api/v1/notes/{id} - Get Note by ID
210
+ // ============================================
211
+ describe('GET /api/v1/notes/{id} - Get Note by ID', () => {
212
+ it('NOTE_API_020: Should get note by valid ID', () => {
213
+ // First create a note
214
+ const noteData = noteAPI.generateRandomData()
215
+
216
+ noteAPI.create(noteData).then((createResponse: any) => {
217
+ expect(createResponse.status).to.eq(201)
218
+ createdNotes.push(createResponse.body.data)
219
+
220
+ const noteId = createResponse.body.data.id
221
+
222
+ // Get the note by ID
223
+ noteAPI.getById(noteId).then((response: any) => {
224
+ noteAPI.validateSuccessResponse(response, 200)
225
+
226
+ const note = response.body.data
227
+ noteAPI.validateObject(note)
228
+ expect(note.id).to.eq(noteId)
229
+ expect(note.content).to.eq(noteData.content)
230
+
231
+ cy.log(`Retrieved note: ${note.id}`)
232
+ })
233
+ })
234
+ })
235
+
236
+ it('NOTE_API_021: Should return 404 for non-existent note', () => {
237
+ const fakeId = 'non-existent-note-id-12345'
238
+
239
+ noteAPI.getById(fakeId).then((response: any) => {
240
+ expect(response.status).to.eq(404)
241
+ expect(response.body).to.have.property('success', false)
242
+
243
+ cy.log('Non-existent note returns 404')
244
+ })
245
+ })
246
+ })
247
+
248
+ // ============================================
249
+ // PATCH /api/v1/notes/{id} - Update Note
250
+ // ============================================
251
+ describe('PATCH /api/v1/notes/{id} - Update Note', () => {
252
+ it('NOTE_API_030: Should update note with multiple fields', () => {
253
+ // First create a note
254
+ noteAPI.createTestRecord({}, { withRetry: true }).then((testNote: any) => {
255
+ createdNotes.push(testNote)
256
+
257
+ const updateData = {
258
+ content: 'Updated note content with new information',
259
+ type: 'call',
260
+ isPinned: true
261
+ }
262
+
263
+ noteAPI.update(testNote.id, updateData).then((response: any) => {
264
+ noteAPI.validateSuccessResponse(response, 200)
265
+
266
+ const note = response.body.data
267
+ expect(note.content).to.eq(updateData.content)
268
+ expect(note.type).to.eq(updateData.type)
269
+ expect(note.isPinned).to.eq(updateData.isPinned)
270
+
271
+ cy.log(`Updated note: ${note.id}`)
272
+ })
273
+ })
274
+ })
275
+
276
+ it('NOTE_API_031: Should update note content', () => {
277
+ noteAPI.createTestRecord({}, { withRetry: true }).then((testNote: any) => {
278
+ createdNotes.push(testNote)
279
+
280
+ const newContent = 'This is the updated content for the note'
281
+
282
+ noteAPI.update(testNote.id, { content: newContent }).then((response: any) => {
283
+ noteAPI.validateSuccessResponse(response, 200)
284
+ expect(response.body.data.content).to.eq(newContent)
285
+
286
+ cy.log(`Updated content`)
287
+ })
288
+ })
289
+ })
290
+
291
+ it('NOTE_API_032: Should update note type and pinned status', () => {
292
+ noteAPI.createTestRecord({}, { withRetry: true }).then((testNote: any) => {
293
+ createdNotes.push(testNote)
294
+
295
+ const updateData = {
296
+ type: 'reminder',
297
+ isPinned: true,
298
+ isPrivate: true
299
+ }
300
+
301
+ noteAPI.update(testNote.id, updateData).then((response: any) => {
302
+ noteAPI.validateSuccessResponse(response, 200)
303
+ expect(response.body.data.type).to.eq(updateData.type)
304
+ expect(response.body.data.isPinned).to.eq(updateData.isPinned)
305
+ expect(response.body.data.isPrivate).to.eq(updateData.isPrivate)
306
+
307
+ cy.log(`Updated note type and status`)
308
+ })
309
+ })
310
+ })
311
+
312
+ it('NOTE_API_033: Should return 404 for non-existent note', () => {
313
+ const fakeId = 'non-existent-note-id-12345'
314
+
315
+ noteAPI.update(fakeId, { content: 'New Content' }).then((response: any) => {
316
+ expect(response.status).to.eq(404)
317
+ expect(response.body).to.have.property('success', false)
318
+
319
+ cy.log('Update non-existent note returns 404')
320
+ })
321
+ })
322
+ })
323
+
324
+ // ============================================
325
+ // DELETE /api/v1/notes/{id} - Delete Note
326
+ // ============================================
327
+ describe('DELETE /api/v1/notes/{id} - Delete Note', () => {
328
+ it('NOTE_API_040: Should delete note by valid ID', () => {
329
+ // Create a note to delete
330
+ const noteData = noteAPI.generateRandomData()
331
+
332
+ noteAPI.create(noteData).then((createResponse: any) => {
333
+ expect(createResponse.status).to.eq(201)
334
+ const noteId = createResponse.body.data.id
335
+
336
+ // Delete the note
337
+ noteAPI.delete(noteId).then((response: any) => {
338
+ noteAPI.validateSuccessResponse(response, 200)
339
+ expect(response.body.data).to.have.property('success', true)
340
+ expect(response.body.data).to.have.property('id', noteId)
341
+
342
+ cy.log(`Deleted note: ${noteId}`)
343
+ })
344
+ })
345
+ })
346
+
347
+ it('NOTE_API_041: Should return 404 for non-existent note', () => {
348
+ const fakeId = 'non-existent-note-id-12345'
349
+
350
+ noteAPI.delete(fakeId).then((response: any) => {
351
+ expect(response.status).to.eq(404)
352
+ expect(response.body).to.have.property('success', false)
353
+
354
+ cy.log('Delete non-existent note returns 404')
355
+ })
356
+ })
357
+ })
358
+
359
+ // ============================================
360
+ // Integration - Complete CRUD Lifecycle
361
+ // ============================================
362
+ describe('Integration - Complete CRUD Lifecycle', () => {
363
+ it('NOTE_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {
364
+ // 1. CREATE
365
+ const noteData = noteAPI.generateRandomData({
366
+ title: 'Lifecycle Test Note',
367
+ content: 'Initial note for lifecycle testing',
368
+ type: 'meeting',
369
+ isPinned: false
370
+ })
371
+
372
+ noteAPI.create(noteData).then((createResponse: any) => {
373
+ noteAPI.validateSuccessResponse(createResponse, 201)
374
+ const noteId = createResponse.body.data.id
375
+
376
+ cy.log(`1. Created note: ${noteId}`)
377
+
378
+ // 2. READ
379
+ noteAPI.getById(noteId).then((readResponse: any) => {
380
+ noteAPI.validateSuccessResponse(readResponse, 200)
381
+ expect(readResponse.body.data.content).to.eq(noteData.content)
382
+ expect(readResponse.body.data.type).to.eq(noteData.type)
383
+
384
+ cy.log(`2. Read note: ${noteId}`)
385
+
386
+ // 3. UPDATE
387
+ const updateData = {
388
+ title: 'Updated Lifecycle Note',
389
+ content: 'Updated lifecycle note content',
390
+ type: 'followup',
391
+ isPinned: true,
392
+ isPrivate: true
393
+ }
394
+
395
+ noteAPI.update(noteId, updateData).then((updateResponse: any) => {
396
+ noteAPI.validateSuccessResponse(updateResponse, 200)
397
+ expect(updateResponse.body.data.content).to.eq(updateData.content)
398
+ expect(updateResponse.body.data.title).to.eq(updateData.title)
399
+ expect(updateResponse.body.data.type).to.eq(updateData.type)
400
+ expect(updateResponse.body.data.isPinned).to.eq(updateData.isPinned)
401
+
402
+ cy.log(`3. Updated note`)
403
+
404
+ // 4. DELETE
405
+ noteAPI.delete(noteId).then((deleteResponse: any) => {
406
+ noteAPI.validateSuccessResponse(deleteResponse, 200)
407
+ expect(deleteResponse.body.data).to.have.property('success', true)
408
+
409
+ cy.log(`4. Deleted note: ${noteId}`)
410
+
411
+ // 5. VERIFY DELETION
412
+ noteAPI.getById(noteId).then((verifyResponse: any) => {
413
+ expect(verifyResponse.status).to.eq(404)
414
+
415
+ cy.log('5. Verified deletion - note no longer exists')
416
+ cy.log('Full CRUD lifecycle completed successfully!')
417
+ })
418
+ })
419
+ })
420
+ })
421
+ })
422
+ })
423
+ })
424
+ })