@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.
- 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,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Products API - CRUD Tests
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive test suite for Product API endpoints.
|
|
5
|
+
* Tests GET, POST, PATCH, DELETE operations.
|
|
6
|
+
*
|
|
7
|
+
* Entity characteristics:
|
|
8
|
+
* - Required fields: name, price
|
|
9
|
+
* - Optional fields: sku, category, description, isActive
|
|
10
|
+
* - Access: shared within team (all team members see all products)
|
|
11
|
+
* - Team context: required (x-team-id header)
|
|
12
|
+
* - Type: Config entity (typically lower test count)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/// <reference types="cypress" />
|
|
16
|
+
|
|
17
|
+
import { ProductAPIController } from '../../../src/controllers'
|
|
18
|
+
|
|
19
|
+
describe('Products 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 productAPI: InstanceType<typeof ProductAPIController>
|
|
27
|
+
|
|
28
|
+
// Track created products for cleanup
|
|
29
|
+
let createdProducts: any[] = []
|
|
30
|
+
|
|
31
|
+
before(() => {
|
|
32
|
+
// Initialize controller with superadmin credentials
|
|
33
|
+
productAPI = new ProductAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
// Cleanup created products after each test
|
|
38
|
+
createdProducts.forEach((product) => {
|
|
39
|
+
if (product?.id) {
|
|
40
|
+
productAPI.delete(product.id)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
createdProducts = []
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// ============================================
|
|
47
|
+
// GET /api/v1/products - List Products
|
|
48
|
+
// ============================================
|
|
49
|
+
describe('GET /api/v1/products - List Products', () => {
|
|
50
|
+
it('PROD_API_001: Should list products with valid API key', () => {
|
|
51
|
+
productAPI.getAll().then((response: any) => {
|
|
52
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
53
|
+
expect(response.body.data).to.be.an('array')
|
|
54
|
+
expect(response.body.info).to.have.property('page')
|
|
55
|
+
expect(response.body.info).to.have.property('limit')
|
|
56
|
+
expect(response.body.info).to.have.property('total')
|
|
57
|
+
expect(response.body.info).to.have.property('totalPages')
|
|
58
|
+
|
|
59
|
+
cy.log(`Found ${response.body.data.length} products`)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('PROD_API_002: Should list products with pagination', () => {
|
|
64
|
+
productAPI.getAll({ page: 1, limit: 5 }).then((response: any) => {
|
|
65
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
66
|
+
expect(response.body.info.page).to.eq(1)
|
|
67
|
+
expect(response.body.info.limit).to.eq(5)
|
|
68
|
+
expect(response.body.data.length).to.be.at.most(5)
|
|
69
|
+
|
|
70
|
+
cy.log(`Page 1 with limit 5: ${response.body.data.length} products`)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('PROD_API_003: Should filter products by category', () => {
|
|
75
|
+
// First create a product with a specific category
|
|
76
|
+
const testCategory = 'Software'
|
|
77
|
+
const productData = productAPI.generateRandomData({ category: testCategory })
|
|
78
|
+
|
|
79
|
+
productAPI.create(productData).then((createResponse: any) => {
|
|
80
|
+
expect(createResponse.status).to.eq(201)
|
|
81
|
+
createdProducts.push(createResponse.body.data)
|
|
82
|
+
|
|
83
|
+
// Now filter by that category
|
|
84
|
+
productAPI.getAll({ category: testCategory }).then((response: any) => {
|
|
85
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
86
|
+
expect(response.body.data).to.be.an('array')
|
|
87
|
+
|
|
88
|
+
// All returned products should have the specified category
|
|
89
|
+
response.body.data.forEach((product: any) => {
|
|
90
|
+
expect(product.category).to.eq(testCategory)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
cy.log(`Found ${response.body.data.length} products with category '${testCategory}'`)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('PROD_API_004: Should filter products by active status', () => {
|
|
99
|
+
// First create active and inactive products
|
|
100
|
+
const activeProduct = productAPI.generateRandomData({ isActive: true })
|
|
101
|
+
const inactiveProduct = productAPI.generateRandomData({ isActive: false })
|
|
102
|
+
|
|
103
|
+
productAPI.create(activeProduct).then((createResponse1: any) => {
|
|
104
|
+
expect(createResponse1.status).to.eq(201)
|
|
105
|
+
createdProducts.push(createResponse1.body.data)
|
|
106
|
+
|
|
107
|
+
productAPI.create(inactiveProduct).then((createResponse2: any) => {
|
|
108
|
+
expect(createResponse2.status).to.eq(201)
|
|
109
|
+
createdProducts.push(createResponse2.body.data)
|
|
110
|
+
|
|
111
|
+
// Filter by active status
|
|
112
|
+
productAPI.getAll({ isActive: true }).then((response: any) => {
|
|
113
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
114
|
+
expect(response.body.data).to.be.an('array')
|
|
115
|
+
|
|
116
|
+
// Verify our active product is in results
|
|
117
|
+
const foundActive = response.body.data.find(
|
|
118
|
+
(p: any) => p.id === createResponse1.body.data.id
|
|
119
|
+
)
|
|
120
|
+
expect(foundActive).to.exist
|
|
121
|
+
expect(foundActive.isActive).to.be.true
|
|
122
|
+
|
|
123
|
+
cy.log(`Found ${response.body.data.length} active products`)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// ============================================
|
|
131
|
+
// POST /api/v1/products - Create Product
|
|
132
|
+
// ============================================
|
|
133
|
+
describe('POST /api/v1/products - Create Product', () => {
|
|
134
|
+
it('PROD_API_010: Should create product with valid data', () => {
|
|
135
|
+
const productData = productAPI.generateRandomData({
|
|
136
|
+
code: 'ENT-LIC-001',
|
|
137
|
+
name: 'Enterprise License',
|
|
138
|
+
price: 9999.99,
|
|
139
|
+
category: 'Software',
|
|
140
|
+
description: 'Full enterprise license with all features',
|
|
141
|
+
isActive: true
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
productAPI.create(productData).then((response: any) => {
|
|
145
|
+
productAPI.validateSuccessResponse(response, 201)
|
|
146
|
+
createdProducts.push(response.body.data)
|
|
147
|
+
|
|
148
|
+
const product = response.body.data
|
|
149
|
+
productAPI.validateObject(product)
|
|
150
|
+
|
|
151
|
+
// Verify provided data
|
|
152
|
+
expect(product.name).to.eq(productData.name)
|
|
153
|
+
expect(product.price).to.satisfy((val: any) =>
|
|
154
|
+
typeof val === 'number' ? val === productData.price : parseFloat(val) === productData.price
|
|
155
|
+
)
|
|
156
|
+
expect(product.code).to.eq(productData.code)
|
|
157
|
+
expect(product.category).to.eq(productData.category)
|
|
158
|
+
expect(product.description).to.eq(productData.description)
|
|
159
|
+
expect(product.isActive).to.eq(productData.isActive)
|
|
160
|
+
|
|
161
|
+
cy.log(`Created product: ${product.name} (ID: ${product.id})`)
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('PROD_API_011: Should create product with minimal data (code, name, price only)', () => {
|
|
166
|
+
const minimalData = {
|
|
167
|
+
code: `MIN-${Date.now()}`,
|
|
168
|
+
name: `Minimal Product ${Date.now()}`,
|
|
169
|
+
price: 99.99
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
productAPI.create(minimalData).then((response: any) => {
|
|
173
|
+
productAPI.validateSuccessResponse(response, 201)
|
|
174
|
+
createdProducts.push(response.body.data)
|
|
175
|
+
|
|
176
|
+
const product = response.body.data
|
|
177
|
+
productAPI.validateObject(product)
|
|
178
|
+
|
|
179
|
+
// Verify required fields
|
|
180
|
+
expect(product.name).to.eq(minimalData.name)
|
|
181
|
+
expect(product.price).to.satisfy((val: any) =>
|
|
182
|
+
typeof val === 'number' ? val === minimalData.price : parseFloat(val) === minimalData.price
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
cy.log(`Created product with minimal data: ${product.id}`)
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('PROD_API_012: Should create product with all optional fields', () => {
|
|
190
|
+
const productData = productAPI.generateRandomData({
|
|
191
|
+
code: 'COMPLETE-001',
|
|
192
|
+
name: `Complete Product ${Date.now()}`,
|
|
193
|
+
price: 4999.99,
|
|
194
|
+
category: 'Services',
|
|
195
|
+
description: 'Product with all fields populated',
|
|
196
|
+
isActive: true
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
productAPI.create(productData).then((response: any) => {
|
|
200
|
+
productAPI.validateSuccessResponse(response, 201)
|
|
201
|
+
createdProducts.push(response.body.data)
|
|
202
|
+
|
|
203
|
+
const product = response.body.data
|
|
204
|
+
|
|
205
|
+
// Verify all fields
|
|
206
|
+
expect(product.name).to.eq(productData.name)
|
|
207
|
+
expect(product.code).to.eq(productData.code)
|
|
208
|
+
expect(product.category).to.eq(productData.category)
|
|
209
|
+
expect(product.description).to.eq(productData.description)
|
|
210
|
+
expect(product.isActive).to.eq(productData.isActive)
|
|
211
|
+
|
|
212
|
+
cy.log(`Created product with all fields: ${product.id}`)
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('PROD_API_013: Should reject creation without name', () => {
|
|
217
|
+
const invalidData = {
|
|
218
|
+
code: 'NO-NAME-001',
|
|
219
|
+
price: 99.99
|
|
220
|
+
// Missing: name
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
productAPI.create(invalidData).then((response: any) => {
|
|
224
|
+
productAPI.validateErrorResponse(response, 400, 'VALIDATION_ERROR')
|
|
225
|
+
|
|
226
|
+
cy.log('Creation without name rejected with VALIDATION_ERROR')
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// ============================================
|
|
232
|
+
// GET /api/v1/products/{id} - Get Product by ID
|
|
233
|
+
// ============================================
|
|
234
|
+
describe('GET /api/v1/products/{id} - Get Product by ID', () => {
|
|
235
|
+
it('PROD_API_020: Should get product by valid ID', () => {
|
|
236
|
+
// First create a product
|
|
237
|
+
const productData = productAPI.generateRandomData()
|
|
238
|
+
|
|
239
|
+
productAPI.create(productData).then((createResponse: any) => {
|
|
240
|
+
expect(createResponse.status).to.eq(201)
|
|
241
|
+
createdProducts.push(createResponse.body.data)
|
|
242
|
+
|
|
243
|
+
const productId = createResponse.body.data.id
|
|
244
|
+
|
|
245
|
+
// Get the product by ID
|
|
246
|
+
productAPI.getById(productId).then((response: any) => {
|
|
247
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
248
|
+
|
|
249
|
+
const product = response.body.data
|
|
250
|
+
productAPI.validateObject(product)
|
|
251
|
+
expect(product.id).to.eq(productId)
|
|
252
|
+
expect(product.name).to.eq(productData.name)
|
|
253
|
+
|
|
254
|
+
cy.log(`Retrieved product: ${product.name}`)
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it('PROD_API_021: Should return 404 for non-existent product', () => {
|
|
260
|
+
const fakeId = 'non-existent-product-id-12345'
|
|
261
|
+
|
|
262
|
+
productAPI.getById(fakeId).then((response: any) => {
|
|
263
|
+
expect(response.status).to.eq(404)
|
|
264
|
+
expect(response.body).to.have.property('success', false)
|
|
265
|
+
|
|
266
|
+
cy.log('Non-existent product returns 404')
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
// ============================================
|
|
272
|
+
// PATCH /api/v1/products/{id} - Update Product
|
|
273
|
+
// ============================================
|
|
274
|
+
describe('PATCH /api/v1/products/{id} - Update Product', () => {
|
|
275
|
+
it('PROD_API_030: Should update product with multiple fields', () => {
|
|
276
|
+
// First create a product
|
|
277
|
+
productAPI.createTestRecord().then((testProduct: any) => {
|
|
278
|
+
createdProducts.push(testProduct)
|
|
279
|
+
|
|
280
|
+
const updateData = {
|
|
281
|
+
name: 'Updated Product Name',
|
|
282
|
+
price: 1999.99,
|
|
283
|
+
category: 'Training',
|
|
284
|
+
description: 'Updated product description'
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
productAPI.update(testProduct.id, updateData).then((response: any) => {
|
|
288
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
289
|
+
|
|
290
|
+
const product = response.body.data
|
|
291
|
+
expect(product.name).to.eq(updateData.name)
|
|
292
|
+
expect(product.category).to.eq(updateData.category)
|
|
293
|
+
expect(product.description).to.eq(updateData.description)
|
|
294
|
+
|
|
295
|
+
cy.log(`Updated product: ${product.name}`)
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('PROD_API_031: Should update product price', () => {
|
|
301
|
+
productAPI.createTestRecord().then((testProduct: any) => {
|
|
302
|
+
createdProducts.push(testProduct)
|
|
303
|
+
|
|
304
|
+
const newPrice = 2999.99
|
|
305
|
+
|
|
306
|
+
productAPI.update(testProduct.id, { price: newPrice }).then((response: any) => {
|
|
307
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
308
|
+
expect(response.body.data.price).to.satisfy((val: any) =>
|
|
309
|
+
typeof val === 'number' ? val === newPrice : parseFloat(val) === newPrice
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
cy.log(`Updated price to: ${newPrice}`)
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it('PROD_API_032: Should update product active status', () => {
|
|
318
|
+
productAPI.createTestRecord().then((testProduct: any) => {
|
|
319
|
+
createdProducts.push(testProduct)
|
|
320
|
+
|
|
321
|
+
const newStatus = false
|
|
322
|
+
|
|
323
|
+
productAPI.update(testProduct.id, { isActive: newStatus }).then((response: any) => {
|
|
324
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
325
|
+
expect(response.body.data.isActive).to.eq(newStatus)
|
|
326
|
+
|
|
327
|
+
cy.log(`Updated isActive to: ${newStatus}`)
|
|
328
|
+
})
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
it('PROD_API_033: Should return 404 for non-existent product', () => {
|
|
333
|
+
const fakeId = 'non-existent-product-id-12345'
|
|
334
|
+
|
|
335
|
+
productAPI.update(fakeId, { name: 'New Name' }).then((response: any) => {
|
|
336
|
+
expect(response.status).to.eq(404)
|
|
337
|
+
expect(response.body).to.have.property('success', false)
|
|
338
|
+
|
|
339
|
+
cy.log('Update non-existent product returns 404')
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
// ============================================
|
|
345
|
+
// DELETE /api/v1/products/{id} - Delete Product
|
|
346
|
+
// ============================================
|
|
347
|
+
describe('DELETE /api/v1/products/{id} - Delete Product', () => {
|
|
348
|
+
it('PROD_API_040: Should delete product by valid ID', () => {
|
|
349
|
+
// Create a product to delete
|
|
350
|
+
const productData = productAPI.generateRandomData()
|
|
351
|
+
|
|
352
|
+
productAPI.create(productData).then((createResponse: any) => {
|
|
353
|
+
expect(createResponse.status).to.eq(201)
|
|
354
|
+
const productId = createResponse.body.data.id
|
|
355
|
+
|
|
356
|
+
// Delete the product
|
|
357
|
+
productAPI.delete(productId).then((response: any) => {
|
|
358
|
+
productAPI.validateSuccessResponse(response, 200)
|
|
359
|
+
expect(response.body.data).to.have.property('success', true)
|
|
360
|
+
expect(response.body.data).to.have.property('id', productId)
|
|
361
|
+
|
|
362
|
+
cy.log(`Deleted product: ${productId}`)
|
|
363
|
+
})
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
it('PROD_API_041: Should return 404 for non-existent product', () => {
|
|
368
|
+
const fakeId = 'non-existent-product-id-12345'
|
|
369
|
+
|
|
370
|
+
productAPI.delete(fakeId).then((response: any) => {
|
|
371
|
+
expect(response.status).to.eq(404)
|
|
372
|
+
expect(response.body).to.have.property('success', false)
|
|
373
|
+
|
|
374
|
+
cy.log('Delete non-existent product returns 404')
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
// ============================================
|
|
380
|
+
// Integration - Complete CRUD Lifecycle
|
|
381
|
+
// ============================================
|
|
382
|
+
describe('Integration - Complete CRUD Lifecycle', () => {
|
|
383
|
+
it('PROD_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {
|
|
384
|
+
// 1. CREATE
|
|
385
|
+
const productData = productAPI.generateRandomData({
|
|
386
|
+
code: 'LIFECYCLE-001',
|
|
387
|
+
name: 'Lifecycle Test Product',
|
|
388
|
+
price: 1999.99,
|
|
389
|
+
category: 'Software',
|
|
390
|
+
description: 'Initial product for lifecycle testing',
|
|
391
|
+
isActive: true
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
productAPI.create(productData).then((createResponse: any) => {
|
|
395
|
+
productAPI.validateSuccessResponse(createResponse, 201)
|
|
396
|
+
const productId = createResponse.body.data.id
|
|
397
|
+
|
|
398
|
+
cy.log(`1. Created product: ${productId}`)
|
|
399
|
+
|
|
400
|
+
// 2. READ
|
|
401
|
+
productAPI.getById(productId).then((readResponse: any) => {
|
|
402
|
+
productAPI.validateSuccessResponse(readResponse, 200)
|
|
403
|
+
expect(readResponse.body.data.name).to.eq(productData.name)
|
|
404
|
+
expect(readResponse.body.data.code).to.eq(productData.code)
|
|
405
|
+
|
|
406
|
+
cy.log(`2. Read product: ${readResponse.body.data.name}`)
|
|
407
|
+
|
|
408
|
+
// 3. UPDATE
|
|
409
|
+
const updateData = {
|
|
410
|
+
name: 'Updated Lifecycle Product',
|
|
411
|
+
price: 2999.99,
|
|
412
|
+
code: 'LIFECYCLE-002',
|
|
413
|
+
category: 'Services',
|
|
414
|
+
description: 'Updated product description',
|
|
415
|
+
isActive: false
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
productAPI.update(productId, updateData).then((updateResponse: any) => {
|
|
419
|
+
productAPI.validateSuccessResponse(updateResponse, 200)
|
|
420
|
+
expect(updateResponse.body.data.name).to.eq(updateData.name)
|
|
421
|
+
expect(updateResponse.body.data.code).to.eq(updateData.code)
|
|
422
|
+
expect(updateResponse.body.data.category).to.eq(updateData.category)
|
|
423
|
+
expect(updateResponse.body.data.isActive).to.eq(updateData.isActive)
|
|
424
|
+
|
|
425
|
+
cy.log(`3. Updated product: ${updateResponse.body.data.name}`)
|
|
426
|
+
|
|
427
|
+
// 4. DELETE
|
|
428
|
+
productAPI.delete(productId).then((deleteResponse: any) => {
|
|
429
|
+
productAPI.validateSuccessResponse(deleteResponse, 200)
|
|
430
|
+
expect(deleteResponse.body.data).to.have.property('success', true)
|
|
431
|
+
|
|
432
|
+
cy.log(`4. Deleted product: ${productId}`)
|
|
433
|
+
|
|
434
|
+
// 5. VERIFY DELETION
|
|
435
|
+
productAPI.getById(productId).then((verifyResponse: any) => {
|
|
436
|
+
expect(verifyResponse.status).to.eq(404)
|
|
437
|
+
|
|
438
|
+
cy.log('5. Verified deletion - product no longer exists')
|
|
439
|
+
cy.log('Full CRUD lifecycle completed successfully!')
|
|
440
|
+
})
|
|
441
|
+
})
|
|
442
|
+
})
|
|
443
|
+
})
|
|
444
|
+
})
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
})
|