@nextsparkjs/theme-crm 0.1.0-beta.19 → 0.1.0-beta.24

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 (74) hide show
  1. package/package.json +3 -3
  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/jest/__mocks__/jose.js +22 -0
  71. package/tests/jest/__mocks__/next-server.js +56 -0
  72. package/tests/jest/jest.config.cjs +127 -0
  73. package/tests/jest/setup.ts +170 -0
  74. package/tests/tsconfig.json +15 -0
@@ -0,0 +1,262 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Pipelines - Member Role (Read Only Access)
5
+ *
6
+ * Uses CRM theme-specific POMs with Entity Testing Convention.
7
+ * Selectors follow the pattern: {slug}-{component}-{detail}
8
+ */
9
+
10
+ import { PipelinesPOM } from '../../../src/entities/PipelinesPOM'
11
+ import { loginAsCrmMember } from '../../../src/session-helpers'
12
+
13
+ describe('Pipelines - Member Role (Read Only Access)', () => {
14
+ const pipelines = new PipelinesPOM()
15
+
16
+ beforeEach(() => {
17
+ loginAsCrmMember()
18
+ cy.visit('/dashboard/pipelines')
19
+ pipelines.list.validatePageVisible()
20
+ })
21
+
22
+ // ==================== READ ONLY ====================
23
+ describe('READ Pipeline (Allowed)', () => {
24
+ it('MEMBER_PIPE_READ_001: Should display pipelines list', () => {
25
+ pipelines.list.validatePageVisible()
26
+ pipelines.list.validateTableVisible()
27
+ })
28
+
29
+ it('MEMBER_PIPE_READ_002: Should search and filter pipelines', () => {
30
+ pipelines.list.search('Sales')
31
+ cy.wait(500)
32
+ pipelines.list.clearSearch()
33
+ })
34
+
35
+ it('MEMBER_PIPE_READ_003: Should view pipeline details (Kanban)', () => {
36
+ pipelines.list.clickRowByIndex(0)
37
+ cy.url().should('match', /\/pipelines\/[a-zA-Z0-9-]+$/)
38
+ pipelines.validateKanbanVisible()
39
+ })
40
+
41
+ it('MEMBER_PIPE_READ_004: Should paginate through pipelines', () => {
42
+ pipelines.list.validatePageVisible()
43
+
44
+ cy.get(pipelines.list.selectors.pagination).then($pagination => {
45
+ if ($pagination.length > 0) {
46
+ pipelines.list.nextPage()
47
+ }
48
+ })
49
+ })
50
+
51
+ it('MEMBER_PIPE_READ_005: Should view pipelines created by other users', () => {
52
+ // Member should see owner and admin created pipelines
53
+ pipelines.list.validateTableVisible()
54
+ })
55
+
56
+ it('MEMBER_PIPE_READ_006: Should view pipeline stages in Kanban', () => {
57
+ pipelines.list.clickRowByIndex(0)
58
+ pipelines.validateKanbanVisible()
59
+ cy.get('[data-cy^="stage-column-"]').should('have.length.at.least', 1)
60
+ })
61
+ })
62
+
63
+ // ==================== CREATE NOT ALLOWED ====================
64
+ describe('CREATE Pipeline (Denied)', () => {
65
+ it('MEMBER_PIPE_CREATE_001: Should not see create button', () => {
66
+ // Verify create button doesn't exist or is disabled
67
+ cy.get(pipelines.list.selectors.createButton).should('not.exist')
68
+ })
69
+
70
+ it('MEMBER_PIPE_CREATE_002: Should not be able to navigate to create form via URL', () => {
71
+ // Try to access create form directly
72
+ cy.visit('/dashboard/pipelines/create', { failOnStatusCode: false })
73
+
74
+ // Should redirect or show error
75
+ cy.url().then(url => {
76
+ if (url.includes('/create')) {
77
+ // If on create page, should show permission error
78
+ cy.contains(/unauthorized|access denied|permission denied|forbidden/i).should('exist')
79
+ }
80
+ })
81
+ })
82
+
83
+ it('MEMBER_PIPE_CREATE_003: Should show permission error on create attempt via API', () => {
84
+ cy.request({
85
+ method: 'POST',
86
+ url: '/api/v1/pipelines',
87
+ failOnStatusCode: false,
88
+ headers: {
89
+ 'Content-Type': 'application/json'
90
+ },
91
+ body: {
92
+ name: 'Unauthorized Pipeline',
93
+ stages: [{ id: '1', name: 'Test', order: 1, color: '#3b82f6' }]
94
+ }
95
+ }).then(response => {
96
+ expect(response.status).to.be.oneOf([401, 403])
97
+ })
98
+ })
99
+ })
100
+
101
+ // ==================== UPDATE NOT ALLOWED ====================
102
+ describe('UPDATE Pipeline (Denied)', () => {
103
+ it('MEMBER_PIPE_UPDATE_001: Should not see edit button in list', () => {
104
+ // Navigate to a pipeline
105
+ pipelines.list.clickRowByIndex(0)
106
+ pipelines.validateKanbanVisible()
107
+
108
+ // Check if edit button exists in Kanban header
109
+ cy.get('[data-cy*="edit-btn"]').should('not.exist')
110
+ })
111
+
112
+ it('MEMBER_PIPE_UPDATE_002: Should not be able to navigate to edit form via URL', () => {
113
+ // Get first pipeline ID
114
+ pipelines.list.clickRowByIndex(0)
115
+
116
+ cy.url().then(url => {
117
+ const pipelineId = url.split('/').pop()
118
+
119
+ // Try to access edit form directly
120
+ cy.visit(`/dashboard/pipelines/${pipelineId}/edit`, { failOnStatusCode: false })
121
+
122
+ cy.url().then(editUrl => {
123
+ if (editUrl.includes('/edit')) {
124
+ cy.contains(/unauthorized|access denied|permission denied|forbidden/i).should('exist')
125
+ }
126
+ })
127
+ })
128
+ })
129
+
130
+ it('MEMBER_PIPE_UPDATE_003: Should show permission error on update attempt via API', () => {
131
+ cy.request({
132
+ method: 'PATCH',
133
+ url: '/api/v1/pipelines/test-id',
134
+ failOnStatusCode: false,
135
+ headers: {
136
+ 'Content-Type': 'application/json'
137
+ },
138
+ body: {
139
+ name: 'Unauthorized Update'
140
+ }
141
+ }).then(response => {
142
+ expect(response.status).to.be.oneOf([401, 403, 404])
143
+ })
144
+ })
145
+ })
146
+
147
+ // ==================== DELETE NOT ALLOWED ====================
148
+ describe('DELETE Pipeline (Denied)', () => {
149
+ it('MEMBER_PIPE_DELETE_001: Should not see delete button', () => {
150
+ pipelines.list.validateTableVisible()
151
+
152
+ // Verify delete actions don't exist in rows
153
+ cy.get(pipelines.list.selectors.rowGeneric).first().within(() => {
154
+ cy.get('[data-cy*="delete"]').should('not.exist')
155
+ })
156
+ })
157
+
158
+ it('MEMBER_PIPE_DELETE_002: Should not see bulk delete controls', () => {
159
+ // Verify bulk select checkboxes don't allow deletion
160
+ cy.get(pipelines.list.selectors.bulkActions).should('not.exist')
161
+ })
162
+
163
+ it('MEMBER_PIPE_DELETE_003: Should show permission error on delete attempt via API', () => {
164
+ cy.request({
165
+ method: 'DELETE',
166
+ url: '/api/v1/pipelines/test-id',
167
+ failOnStatusCode: false
168
+ }).then(response => {
169
+ expect(response.status).to.be.oneOf([401, 403, 404])
170
+ })
171
+ })
172
+ })
173
+
174
+ // ==================== PERMISSION BOUNDARIES ====================
175
+ describe('Permission Boundaries', () => {
176
+ it('MEMBER_PIPE_PERM_001: Should only have view access in Kanban', () => {
177
+ pipelines.list.clickRowByIndex(0)
178
+ pipelines.validateKanbanVisible()
179
+
180
+ // Should not see edit/delete buttons
181
+ cy.get('[data-cy*="edit-btn"]').should('not.exist')
182
+ cy.get('[data-cy*="delete-btn"]').should('not.exist')
183
+ })
184
+
185
+ it('MEMBER_PIPE_PERM_002: Should be able to view deals in Kanban', () => {
186
+ pipelines.list.clickRowByIndex(0)
187
+ pipelines.validateKanbanVisible()
188
+
189
+ // Should see deal cards (if any exist)
190
+ cy.get(pipelines.kanbanSelectors.boardContainer).should('be.visible')
191
+ })
192
+
193
+ it('MEMBER_PIPE_PERM_003: Should not be able to add deals', () => {
194
+ pipelines.list.clickRowByIndex(0)
195
+ pipelines.validateKanbanVisible()
196
+
197
+ // Add deal button should not exist for members
198
+ cy.get(pipelines.kanbanSelectors.addDealButton).should('not.exist')
199
+ })
200
+
201
+ it('MEMBER_PIPE_PERM_004: Should maintain read-only state during navigation', () => {
202
+ // Check list page
203
+ cy.get(pipelines.list.selectors.createButton).should('not.exist')
204
+
205
+ // Navigate to Kanban
206
+ pipelines.list.clickRowByIndex(0)
207
+ pipelines.validateKanbanVisible()
208
+
209
+ // Navigate back
210
+ cy.visit('/dashboard/pipelines')
211
+ pipelines.list.validatePageVisible()
212
+
213
+ // Still read-only
214
+ cy.get(pipelines.list.selectors.createButton).should('not.exist')
215
+ })
216
+ })
217
+
218
+ // ==================== KANBAN VIEW (Read Only) ====================
219
+ describe('KANBAN View (Read Only)', () => {
220
+ it('MEMBER_PIPE_KANBAN_001: Should display Kanban board', () => {
221
+ pipelines.list.clickRowByIndex(0)
222
+ pipelines.validateKanbanVisible()
223
+ })
224
+
225
+ it('MEMBER_PIPE_KANBAN_002: Should display stats cards', () => {
226
+ pipelines.list.clickRowByIndex(0)
227
+ pipelines.validateStatsVisible()
228
+ })
229
+
230
+ it('MEMBER_PIPE_KANBAN_003: Should display stage columns', () => {
231
+ pipelines.list.clickRowByIndex(0)
232
+ cy.get(pipelines.kanbanSelectors.boardContainer).should('be.visible')
233
+ cy.get('[data-cy^="stage-column-"]').should('have.length.at.least', 1)
234
+ })
235
+
236
+ it('MEMBER_PIPE_KANBAN_004: Should not be able to drag deals', () => {
237
+ pipelines.list.clickRowByIndex(0)
238
+ pipelines.validateKanbanVisible()
239
+
240
+ // Deal cards should not be draggable for members
241
+ cy.get('[data-cy^="deal-card-"]').first().then($card => {
242
+ if ($card.length > 0) {
243
+ // Try dragging - should not work or card should not have draggable attribute
244
+ expect($card.attr('draggable')).to.not.equal('true')
245
+ }
246
+ })
247
+ })
248
+
249
+ it('MEMBER_PIPE_KANBAN_005: Should be able to click deal cards to view details', () => {
250
+ pipelines.list.clickRowByIndex(0)
251
+ pipelines.validateKanbanVisible()
252
+
253
+ // If deal cards exist, clicking should navigate to deal detail
254
+ cy.get('[data-cy^="deal-card-"]').then($cards => {
255
+ if ($cards.length > 0) {
256
+ cy.wrap($cards.first()).click()
257
+ cy.url().should('include', '/opportunities/')
258
+ }
259
+ })
260
+ })
261
+ })
262
+ })
@@ -0,0 +1,282 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Pipelines CRUD - Owner Role (Full Access)
5
+ *
6
+ * Uses CRM theme-specific POMs with Entity Testing Convention.
7
+ * Selectors follow the pattern: {slug}-{component}-{detail}
8
+ */
9
+
10
+ import { PipelinesPOM } from '../../../src/entities/PipelinesPOM'
11
+ import { loginAsCrmOwner } from '../../../src/session-helpers'
12
+
13
+ describe('Pipelines CRUD - Owner Role (Full Access)', () => {
14
+ const pipelines = new PipelinesPOM()
15
+
16
+ beforeEach(() => {
17
+ loginAsCrmOwner()
18
+ cy.visit('/dashboard/pipelines')
19
+ pipelines.list.validatePageVisible()
20
+ })
21
+
22
+ // ==================== CREATE ====================
23
+ describe('CREATE Pipeline', () => {
24
+ it('OWNER_PIPE_CREATE_001: Should create new pipeline with required fields', () => {
25
+ pipelines.list.clickCreate()
26
+ pipelines.validateFormVisible()
27
+
28
+ pipelines.fillPipelineForm({
29
+ name: 'Sales Pipeline 2025'
30
+ })
31
+
32
+ pipelines.submitPipelineForm()
33
+ cy.url().should('include', '/pipelines')
34
+ pipelines.list.validatePageVisible()
35
+ })
36
+
37
+ it('OWNER_PIPE_CREATE_002: Should create pipeline with multiple stages', () => {
38
+ pipelines.list.clickCreate()
39
+ pipelines.validateFormVisible()
40
+
41
+ pipelines.fillPipelineForm({
42
+ name: 'Complex Sales Pipeline'
43
+ })
44
+
45
+ pipelines.validateStagesRepeaterVisible()
46
+
47
+ // Modify default stages
48
+ pipelines.fillStageByIndex(0, { name: 'Lead', probability: 10 })
49
+ pipelines.fillStageByIndex(1, { name: 'Qualified', probability: 25 })
50
+ pipelines.fillStageByIndex(2, { name: 'Proposal', probability: 50 })
51
+ pipelines.fillStageByIndex(3, { name: 'Negotiation', probability: 75 })
52
+ pipelines.fillStageByIndex(4, { name: 'Closed Won', probability: 100 })
53
+
54
+ pipelines.submitPipelineForm()
55
+ cy.url().should('include', '/pipelines')
56
+ })
57
+
58
+ it('OWNER_PIPE_CREATE_003: Should create pipeline with optional fields', () => {
59
+ pipelines.list.clickCreate()
60
+ pipelines.validateFormVisible()
61
+
62
+ pipelines.fillPipelineForm({
63
+ name: 'Default Support Pipeline',
64
+ description: 'Pipeline for customer support tickets'
65
+ })
66
+
67
+ pipelines.submitPipelineForm()
68
+ cy.url().should('include', '/pipelines')
69
+ })
70
+
71
+ it('OWNER_PIPE_CREATE_004: Should show validation error for empty name', () => {
72
+ pipelines.list.clickCreate()
73
+ pipelines.validateFormVisible()
74
+
75
+ // Submit without filling name
76
+ pipelines.submitPipelineForm()
77
+
78
+ // Should stay on create page
79
+ cy.url().should('include', '/create')
80
+ })
81
+
82
+ it('OWNER_PIPE_CREATE_005: Should cancel pipeline creation', () => {
83
+ pipelines.list.clickCreate()
84
+ pipelines.validateFormVisible()
85
+
86
+ pipelines.fillPipelineForm({
87
+ name: 'Cancel Test Pipeline'
88
+ })
89
+
90
+ pipelines.cancelPipelineForm()
91
+ pipelines.list.validatePageVisible()
92
+ })
93
+ })
94
+
95
+ // ==================== READ ====================
96
+ describe('READ Pipeline', () => {
97
+ it('OWNER_PIPE_READ_001: Should display pipelines list', () => {
98
+ pipelines.list.validatePageVisible()
99
+ pipelines.list.validateTableVisible()
100
+ })
101
+
102
+ it('OWNER_PIPE_READ_002: Should search and filter pipelines', () => {
103
+ pipelines.list.search('Sales')
104
+ cy.wait(500)
105
+ pipelines.list.clearSearch()
106
+ })
107
+
108
+ it('OWNER_PIPE_READ_003: Should view pipeline details (Kanban)', () => {
109
+ pipelines.list.clickRowByIndex(0)
110
+ cy.url().should('match', /\/pipelines\/[a-zA-Z0-9-]+$/)
111
+ pipelines.validateKanbanVisible()
112
+ })
113
+
114
+ it('OWNER_PIPE_READ_004: Should paginate through pipelines', () => {
115
+ pipelines.list.validatePageVisible()
116
+
117
+ cy.get(pipelines.list.selectors.pagination).then($pagination => {
118
+ if ($pagination.length > 0) {
119
+ pipelines.list.nextPage()
120
+ }
121
+ })
122
+ })
123
+ })
124
+
125
+ // ==================== UPDATE ====================
126
+ describe('UPDATE Pipeline', () => {
127
+ it('OWNER_PIPE_UPDATE_001: Should update pipeline name', () => {
128
+ pipelines.list.clickRowByIndex(0)
129
+ pipelines.validateKanbanVisible()
130
+
131
+ cy.url().then(url => {
132
+ const pipelineId = url.split('/').pop()
133
+ pipelines.visitEdit(pipelineId!)
134
+ })
135
+
136
+ pipelines.validateFormVisible()
137
+ cy.get(pipelines.formSelectors.nameInput).clear().type('Sales Pipeline 2025 Updated')
138
+
139
+ pipelines.submitPipelineForm()
140
+ cy.url().should('include', '/pipelines')
141
+ })
142
+
143
+ it('OWNER_PIPE_UPDATE_002: Should update pipeline stages', () => {
144
+ pipelines.list.clickRowByIndex(0)
145
+
146
+ cy.url().then(url => {
147
+ const pipelineId = url.split('/').pop()
148
+ pipelines.visitEdit(pipelineId!)
149
+ })
150
+
151
+ pipelines.validateFormVisible()
152
+ pipelines.validateStagesRepeaterVisible()
153
+
154
+ // Add a new stage
155
+ pipelines.addStage()
156
+
157
+ cy.get(pipelines.stagesSelectors.list)
158
+ .find('[data-cy^="stages-repeater-item-"]')
159
+ .last()
160
+ .within(() => {
161
+ cy.get('input[data-cy^="stages-repeater-name-"]').type('Closed Lost')
162
+ cy.get('input[data-cy^="stages-repeater-probability-"]').clear().type('0')
163
+ })
164
+
165
+ pipelines.submitPipelineForm()
166
+ cy.url().should('include', '/pipelines')
167
+ })
168
+
169
+ it('OWNER_PIPE_UPDATE_003: Should update optional fields', () => {
170
+ pipelines.list.clickRowByIndex(0)
171
+
172
+ cy.url().then(url => {
173
+ const pipelineId = url.split('/').pop()
174
+ pipelines.visitEdit(pipelineId!)
175
+ })
176
+
177
+ pipelines.validateFormVisible()
178
+
179
+ cy.get(pipelines.formSelectors.descriptionInput)
180
+ .clear()
181
+ .type('Updated pipeline description')
182
+
183
+ pipelines.submitPipelineForm()
184
+ cy.url().should('include', '/pipelines')
185
+ })
186
+
187
+ it('OWNER_PIPE_UPDATE_004: Should cancel update without saving', () => {
188
+ pipelines.list.clickRowByIndex(0)
189
+
190
+ cy.url().then(url => {
191
+ const pipelineId = url.split('/').pop()
192
+ pipelines.visitEdit(pipelineId!)
193
+ })
194
+
195
+ pipelines.validateFormVisible()
196
+ cy.get(pipelines.formSelectors.nameInput).clear().type('Should Not Be Saved')
197
+
198
+ pipelines.cancelPipelineForm()
199
+ cy.url().should('not.include', '/edit')
200
+ })
201
+ })
202
+
203
+ // ==================== DELETE ====================
204
+ describe('DELETE Pipeline', () => {
205
+ it('OWNER_PIPE_DELETE_001: Should delete pipeline', () => {
206
+ // Create a pipeline to delete
207
+ pipelines.list.clickCreate()
208
+ pipelines.validateFormVisible()
209
+
210
+ pipelines.fillPipelineForm({
211
+ name: 'Pipeline To Delete'
212
+ })
213
+
214
+ pipelines.submitPipelineForm()
215
+ cy.url().should('include', '/pipelines')
216
+
217
+ // Delete it
218
+ pipelines.list.validatePageVisible()
219
+ pipelines.list.search('Pipeline To Delete')
220
+
221
+ cy.get(pipelines.list.selectors.rowGeneric).first().within(() => {
222
+ cy.get('[data-cy*="delete"]').click()
223
+ })
224
+
225
+ pipelines.list.confirmDelete()
226
+
227
+ // Verify deleted
228
+ pipelines.list.search('Pipeline To Delete')
229
+ cy.get(pipelines.list.selectors.emptyState).should('be.visible')
230
+ })
231
+
232
+ it('OWNER_PIPE_DELETE_002: Should cancel deletion', () => {
233
+ pipelines.list.search('Sales')
234
+
235
+ cy.get(pipelines.list.selectors.rowGeneric).first().then($row => {
236
+ cy.wrap($row).within(() => {
237
+ cy.get('[data-cy*="delete"]').click()
238
+ })
239
+ })
240
+
241
+ pipelines.list.cancelDelete()
242
+ pipelines.list.validateTableVisible()
243
+ })
244
+
245
+ it('OWNER_PIPE_DELETE_003: Should handle bulk delete', () => {
246
+ pipelines.list.selectAll()
247
+ pipelines.list.validateBulkActionsVisible()
248
+ })
249
+ })
250
+
251
+ // ==================== KANBAN BOARD ====================
252
+ describe('KANBAN Board', () => {
253
+ it('OWNER_PIPE_KANBAN_001: Should display Kanban board on pipeline detail', () => {
254
+ pipelines.list.clickRowByIndex(0)
255
+ pipelines.validateKanbanVisible()
256
+ pipelines.validateStatsVisible()
257
+ })
258
+
259
+ it('OWNER_PIPE_KANBAN_002: Should display pipeline header with name', () => {
260
+ pipelines.list.clickRowByIndex(0)
261
+ cy.get(pipelines.kanbanSelectors.header).should('be.visible')
262
+ })
263
+
264
+ it('OWNER_PIPE_KANBAN_003: Should display stats cards', () => {
265
+ pipelines.list.clickRowByIndex(0)
266
+ pipelines.validateStatsVisible()
267
+ })
268
+
269
+ it('OWNER_PIPE_KANBAN_004: Should display stage columns', () => {
270
+ pipelines.list.clickRowByIndex(0)
271
+ cy.get(pipelines.kanbanSelectors.boardContainer).should('be.visible')
272
+ cy.get('[data-cy^="stage-column-"]').should('have.length.at.least', 1)
273
+ })
274
+
275
+ it('OWNER_PIPE_KANBAN_005: Should click add deal button', () => {
276
+ pipelines.list.clickRowByIndex(0)
277
+ pipelines.validateKanbanVisible()
278
+ pipelines.clickAddDeal()
279
+ cy.url().should('include', 'opportunities')
280
+ })
281
+ })
282
+ })
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "./blocks.schema.json",
3
+ "_warning": "AUTO-GENERATED by build-registry.mjs - DO NOT EDIT MANUALLY",
4
+ "_theme": "crm",
5
+ "_totalBlocks": 0,
6
+ "available": [],
7
+ "categories": [],
8
+ "byCategory": {}
9
+ }