@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,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
|
+
})
|