@nextsparkjs/theme-crm 0.1.0-beta.19 → 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.
- package/package.json +2 -2
- package/tests/cypress/e2e/api/activities/activities-crud.cy.ts +686 -0
- package/tests/cypress/e2e/api/campaigns/campaigns-crud.cy.ts +592 -0
- package/tests/cypress/e2e/api/companies/companies-crud.cy.ts +682 -0
- package/tests/cypress/e2e/api/contacts/contacts-crud.cy.ts +668 -0
- package/tests/cypress/e2e/api/leads/leads-crud.cy.ts +648 -0
- package/tests/cypress/e2e/api/notes/notes-crud.cy.ts +424 -0
- package/tests/cypress/e2e/api/opportunities/opportunities-crud.cy.ts +865 -0
- package/tests/cypress/e2e/api/pipelines/pipelines-crud.cy.ts +545 -0
- package/tests/cypress/e2e/api/products/products-crud.cy.ts +447 -0
- package/tests/cypress/e2e/ui/activities/activities-admin.cy.ts +268 -0
- package/tests/cypress/e2e/ui/activities/activities-member.cy.ts +257 -0
- package/tests/cypress/e2e/ui/activities/activities-owner.cy.ts +268 -0
- package/tests/cypress/e2e/ui/companies/companies-admin.cy.ts +188 -0
- package/tests/cypress/e2e/ui/companies/companies-member.cy.ts +166 -0
- package/tests/cypress/e2e/ui/companies/companies-owner.cy.ts +189 -0
- package/tests/cypress/e2e/ui/contacts/contacts-admin.cy.ts +252 -0
- package/tests/cypress/e2e/ui/contacts/contacts-member.cy.ts +224 -0
- package/tests/cypress/e2e/ui/contacts/contacts-owner.cy.ts +236 -0
- package/tests/cypress/e2e/ui/leads/leads-admin.cy.ts +286 -0
- package/tests/cypress/e2e/ui/leads/leads-member.cy.ts +193 -0
- package/tests/cypress/e2e/ui/leads/leads-owner.cy.ts +210 -0
- package/tests/cypress/e2e/ui/opportunities/opportunities-admin.cy.ts +197 -0
- package/tests/cypress/e2e/ui/opportunities/opportunities-member.cy.ts +229 -0
- package/tests/cypress/e2e/ui/opportunities/opportunities-owner.cy.ts +196 -0
- package/tests/cypress/e2e/ui/pipelines/pipelines-admin.cy.ts +320 -0
- package/tests/cypress/e2e/ui/pipelines/pipelines-member.cy.ts +262 -0
- package/tests/cypress/e2e/ui/pipelines/pipelines-owner.cy.ts +282 -0
- package/tests/cypress/fixtures/blocks.json +9 -0
- package/tests/cypress/fixtures/entities.json +240 -0
- package/tests/cypress/src/components/CRMDataTable.js +223 -0
- package/tests/cypress/src/components/CRMMobileNav.js +138 -0
- package/tests/cypress/src/components/CRMSidebar.js +145 -0
- package/tests/cypress/src/components/CRMTopBar.js +194 -0
- package/tests/cypress/src/components/DealCard.js +197 -0
- package/tests/cypress/src/components/EntityDetail.ts +290 -0
- package/tests/cypress/src/components/EntityForm.ts +357 -0
- package/tests/cypress/src/components/EntityList.ts +360 -0
- package/tests/cypress/src/components/PipelineKanban.js +204 -0
- package/tests/cypress/src/components/StageColumn.js +196 -0
- package/tests/cypress/src/components/index.js +13 -0
- package/tests/cypress/src/components/index.ts +22 -0
- package/tests/cypress/src/controllers/ActivityAPIController.ts +113 -0
- package/tests/cypress/src/controllers/BaseAPIController.ts +307 -0
- package/tests/cypress/src/controllers/CampaignAPIController.ts +114 -0
- package/tests/cypress/src/controllers/CompanyAPIController.ts +112 -0
- package/tests/cypress/src/controllers/ContactAPIController.ts +104 -0
- package/tests/cypress/src/controllers/LeadAPIController.ts +96 -0
- package/tests/cypress/src/controllers/NoteAPIController.ts +130 -0
- package/tests/cypress/src/controllers/OpportunityAPIController.ts +134 -0
- package/tests/cypress/src/controllers/PipelineAPIController.ts +116 -0
- package/tests/cypress/src/controllers/ProductAPIController.ts +113 -0
- package/tests/cypress/src/controllers/index.ts +35 -0
- package/tests/cypress/src/entities/ActivitiesPOM.ts +130 -0
- package/tests/cypress/src/entities/CompaniesPOM.ts +117 -0
- package/tests/cypress/src/entities/ContactsPOM.ts +117 -0
- package/tests/cypress/src/entities/LeadsPOM.ts +129 -0
- package/tests/cypress/src/entities/OpportunitiesPOM.ts +178 -0
- package/tests/cypress/src/entities/PipelinesPOM.ts +341 -0
- package/tests/cypress/src/entities/index.ts +31 -0
- package/tests/cypress/src/forms/OpportunityForm.js +316 -0
- package/tests/cypress/src/forms/PipelineForm.js +243 -0
- package/tests/cypress/src/forms/index.js +8 -0
- package/tests/cypress/src/index.js +22 -0
- package/tests/cypress/src/index.ts +68 -0
- package/tests/cypress/src/selectors.ts +50 -0
- package/tests/cypress/src/session-helpers.ts +94 -0
- package/tests/cypress/support/e2e.ts +89 -0
- package/tests/cypress.config.ts +165 -0
- 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
|
+
})
|