@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,189 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Companies 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 { CompaniesPOM } from '../../../src/entities/CompaniesPOM'
|
|
11
|
+
import { loginAsCrmOwner } from '../../../src/session-helpers'
|
|
12
|
+
|
|
13
|
+
describe('Companies CRUD - Owner Role (Full Access)', () => {
|
|
14
|
+
const companies = new CompaniesPOM()
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
loginAsCrmOwner()
|
|
18
|
+
cy.visit('/dashboard/companies')
|
|
19
|
+
companies.list.validatePageVisible()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('CREATE - Owner can create companies', () => {
|
|
23
|
+
it('OWNER_COMPANY_CREATE_001: should create new company with required fields', () => {
|
|
24
|
+
const timestamp = Date.now()
|
|
25
|
+
const companyName = `Test Company ${timestamp}`
|
|
26
|
+
|
|
27
|
+
companies.list.clickCreate()
|
|
28
|
+
companies.form.validateFormVisible()
|
|
29
|
+
|
|
30
|
+
companies.fillCompanyForm({
|
|
31
|
+
name: companyName,
|
|
32
|
+
industry: 'technology'
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
companies.submitForm()
|
|
36
|
+
|
|
37
|
+
cy.url().should('include', '/dashboard/companies')
|
|
38
|
+
cy.contains(companyName).should('be.visible')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('OWNER_COMPANY_CREATE_002: should create company with all fields', () => {
|
|
42
|
+
const timestamp = Date.now()
|
|
43
|
+
const companyName = `Full Company ${timestamp}`
|
|
44
|
+
|
|
45
|
+
companies.list.clickCreate()
|
|
46
|
+
companies.form.validateFormVisible()
|
|
47
|
+
|
|
48
|
+
companies.fillCompanyForm({
|
|
49
|
+
name: companyName,
|
|
50
|
+
industry: 'healthcare',
|
|
51
|
+
website: 'https://example.com',
|
|
52
|
+
phone: '+1-555-0123'
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
companies.submitForm()
|
|
56
|
+
|
|
57
|
+
cy.url().should('include', '/dashboard/companies')
|
|
58
|
+
cy.contains(companyName).should('be.visible')
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('READ - Owner can read companies', () => {
|
|
63
|
+
it('OWNER_COMPANY_READ_001: should view company list', () => {
|
|
64
|
+
companies.list.validatePageVisible()
|
|
65
|
+
companies.list.validateTableVisible()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('OWNER_COMPANY_READ_002: should view company details', () => {
|
|
69
|
+
cy.get(companies.list.selectors.rowGeneric).then($rows => {
|
|
70
|
+
if ($rows.length > 0) {
|
|
71
|
+
companies.list.clickRowByIndex(0)
|
|
72
|
+
cy.url().should('match', /\/dashboard\/companies\/[a-z0-9-]+/)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('OWNER_COMPANY_READ_003: should search companies', () => {
|
|
78
|
+
companies.list.search('test')
|
|
79
|
+
cy.wait(500)
|
|
80
|
+
companies.list.clearSearch()
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
describe('UPDATE - Owner can update companies', () => {
|
|
85
|
+
it('OWNER_COMPANY_UPDATE_001: should edit company', () => {
|
|
86
|
+
cy.get(companies.list.selectors.rowGeneric).then($rows => {
|
|
87
|
+
if ($rows.length > 0) {
|
|
88
|
+
companies.list.clickRowByIndex(0)
|
|
89
|
+
|
|
90
|
+
cy.url().then(url => {
|
|
91
|
+
const companyId = url.split('/').pop()
|
|
92
|
+
companies.form.visitEdit(companyId!)
|
|
93
|
+
companies.form.validateFormVisible()
|
|
94
|
+
|
|
95
|
+
const updatedName = `Updated Company ${Date.now()}`
|
|
96
|
+
companies.form.typeInField('name', updatedName)
|
|
97
|
+
|
|
98
|
+
companies.submitForm()
|
|
99
|
+
|
|
100
|
+
cy.url().should('include', '/dashboard/companies')
|
|
101
|
+
cy.contains(updatedName).should('be.visible')
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('OWNER_COMPANY_UPDATE_002: should update company with optional fields', () => {
|
|
108
|
+
cy.get(companies.list.selectors.rowGeneric).then($rows => {
|
|
109
|
+
if ($rows.length > 0) {
|
|
110
|
+
companies.list.clickRowByIndex(0)
|
|
111
|
+
|
|
112
|
+
cy.url().then(url => {
|
|
113
|
+
const companyId = url.split('/').pop()
|
|
114
|
+
companies.form.visitEdit(companyId!)
|
|
115
|
+
companies.form.validateFormVisible()
|
|
116
|
+
|
|
117
|
+
const timestamp = Date.now()
|
|
118
|
+
companies.form.typeInField('website', `https://updated-${timestamp}.com`)
|
|
119
|
+
companies.form.typeInField('phone', '+1-555-9999')
|
|
120
|
+
|
|
121
|
+
companies.submitForm()
|
|
122
|
+
|
|
123
|
+
cy.url().should('include', '/dashboard/companies')
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('OWNER_COMPANY_UPDATE_003: should cancel update without saving', () => {
|
|
130
|
+
cy.get(companies.list.selectors.rowGeneric).then($rows => {
|
|
131
|
+
if ($rows.length > 0) {
|
|
132
|
+
companies.list.clickRowByIndex(0)
|
|
133
|
+
|
|
134
|
+
cy.url().then(url => {
|
|
135
|
+
const companyId = url.split('/').pop()
|
|
136
|
+
companies.form.visitEdit(companyId!)
|
|
137
|
+
companies.form.validateFormVisible()
|
|
138
|
+
|
|
139
|
+
companies.form.typeInField('name', 'ShouldNotSave')
|
|
140
|
+
|
|
141
|
+
companies.form.cancel()
|
|
142
|
+
cy.url().should('not.include', '/edit')
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
describe('DELETE - Owner can delete companies', () => {
|
|
150
|
+
it('OWNER_COMPANY_DELETE_001: should delete company successfully', () => {
|
|
151
|
+
const timestamp = Date.now()
|
|
152
|
+
const companyName = `Delete Test ${timestamp}`
|
|
153
|
+
|
|
154
|
+
companies.list.clickCreate()
|
|
155
|
+
companies.fillCompanyForm({
|
|
156
|
+
name: companyName,
|
|
157
|
+
industry: 'technology'
|
|
158
|
+
})
|
|
159
|
+
companies.submitForm()
|
|
160
|
+
|
|
161
|
+
cy.contains(companyName).should('be.visible')
|
|
162
|
+
|
|
163
|
+
companies.list.search(companyName)
|
|
164
|
+
|
|
165
|
+
cy.get(companies.list.selectors.rowGeneric).first().within(() => {
|
|
166
|
+
cy.get('[data-cy*="delete"]').click()
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
companies.list.confirmDelete()
|
|
170
|
+
|
|
171
|
+
cy.wait(1000)
|
|
172
|
+
companies.list.search(companyName)
|
|
173
|
+
cy.get(companies.list.selectors.emptyState).should('be.visible')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('OWNER_COMPANY_DELETE_002: should cancel deletion', () => {
|
|
177
|
+
cy.get(companies.list.selectors.rowGeneric).then($rows => {
|
|
178
|
+
if ($rows.length > 0) {
|
|
179
|
+
cy.get(companies.list.selectors.rowGeneric).first().within(() => {
|
|
180
|
+
cy.get('[data-cy*="delete"]').click()
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
companies.list.cancelDelete()
|
|
184
|
+
companies.list.validateTableVisible()
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
})
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Contacts CRUD - Admin 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 { ContactsPOM } from '../../../src/entities/ContactsPOM'
|
|
11
|
+
import { loginAsCrmAdmin } from '../../../src/session-helpers'
|
|
12
|
+
|
|
13
|
+
describe('Contacts CRUD - Admin Role (Full Access)', () => {
|
|
14
|
+
const contacts = new ContactsPOM()
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
loginAsCrmAdmin()
|
|
18
|
+
cy.visit('/dashboard/contacts')
|
|
19
|
+
contacts.list.validatePageVisible()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('CREATE - Admin can create contacts', () => {
|
|
23
|
+
it('ADMIN_CONTACT_CREATE_001: should create new contact with required fields', () => {
|
|
24
|
+
const firstName = `Sofia${Date.now()}`
|
|
25
|
+
const lastName = 'TestAdmin'
|
|
26
|
+
const email = `sofia.admin.${Date.now()}@test.com`
|
|
27
|
+
|
|
28
|
+
contacts.list.clickCreate()
|
|
29
|
+
contacts.form.validateFormVisible()
|
|
30
|
+
|
|
31
|
+
contacts.fillContactForm({
|
|
32
|
+
firstName,
|
|
33
|
+
lastName,
|
|
34
|
+
email
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
contacts.submitForm()
|
|
38
|
+
|
|
39
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
40
|
+
cy.contains(firstName).should('be.visible')
|
|
41
|
+
cy.contains(lastName).should('be.visible')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('ADMIN_CONTACT_CREATE_002: should create contact with business fields', () => {
|
|
45
|
+
const timestamp = Date.now()
|
|
46
|
+
|
|
47
|
+
contacts.list.clickCreate()
|
|
48
|
+
contacts.form.validateFormVisible()
|
|
49
|
+
|
|
50
|
+
contacts.fillContactForm({
|
|
51
|
+
firstName: `Maria${timestamp}`,
|
|
52
|
+
lastName: 'Gonzalez',
|
|
53
|
+
email: `maria.admin.${timestamp}@test.com`,
|
|
54
|
+
phone: '+34 91 555 1234',
|
|
55
|
+
title: 'Marketing Manager'
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
contacts.submitForm()
|
|
59
|
+
|
|
60
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
61
|
+
cy.contains(`Maria${timestamp}`).should('be.visible')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('ADMIN_CONTACT_CREATE_003: should validate email uniqueness', () => {
|
|
65
|
+
const firstName = `Unique${Date.now()}`
|
|
66
|
+
const lastName = 'Test'
|
|
67
|
+
const email = `unique.admin.${Date.now()}@test.com`
|
|
68
|
+
|
|
69
|
+
// Create first contact
|
|
70
|
+
contacts.list.clickCreate()
|
|
71
|
+
contacts.fillContactForm({ firstName, lastName, email })
|
|
72
|
+
contacts.submitForm()
|
|
73
|
+
|
|
74
|
+
cy.contains(firstName).should('be.visible')
|
|
75
|
+
|
|
76
|
+
// Return to list and try creating with same email
|
|
77
|
+
cy.visit('/dashboard/contacts')
|
|
78
|
+
contacts.list.validatePageVisible()
|
|
79
|
+
|
|
80
|
+
contacts.list.clickCreate()
|
|
81
|
+
contacts.fillContactForm({
|
|
82
|
+
firstName: 'Duplicate',
|
|
83
|
+
lastName: 'Contact',
|
|
84
|
+
email
|
|
85
|
+
})
|
|
86
|
+
contacts.submitForm()
|
|
87
|
+
|
|
88
|
+
// Should show error or stay on form
|
|
89
|
+
cy.get('body').then($body => {
|
|
90
|
+
const isOnCreatePage = window.location.pathname.includes('/create')
|
|
91
|
+
const hasErrorMessage = $body.text().includes('already exists') || $body.text().includes('duplicate')
|
|
92
|
+
|
|
93
|
+
if (isOnCreatePage || hasErrorMessage) {
|
|
94
|
+
cy.log('Email uniqueness validation working')
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('ADMIN_CONTACT_CREATE_004: should cancel contact creation', () => {
|
|
100
|
+
contacts.list.clickCreate()
|
|
101
|
+
contacts.form.validateFormVisible()
|
|
102
|
+
|
|
103
|
+
contacts.fillContactForm({
|
|
104
|
+
firstName: 'Cancel',
|
|
105
|
+
lastName: 'Test'
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
contacts.form.cancel()
|
|
109
|
+
contacts.list.validatePageVisible()
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe('READ - Admin can read contacts', () => {
|
|
114
|
+
it('ADMIN_CONTACT_READ_001: should view contact list', () => {
|
|
115
|
+
contacts.list.validatePageVisible()
|
|
116
|
+
contacts.list.validateTableVisible()
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('ADMIN_CONTACT_READ_002: should view contact details', () => {
|
|
120
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
121
|
+
if ($rows.length > 0) {
|
|
122
|
+
contacts.list.clickRowByIndex(0)
|
|
123
|
+
cy.url().should('match', /\/dashboard\/contacts\/[a-z0-9-]+/)
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('ADMIN_CONTACT_READ_003: should search and filter contacts', () => {
|
|
129
|
+
contacts.list.search('test')
|
|
130
|
+
cy.wait(500)
|
|
131
|
+
contacts.list.clearSearch()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('ADMIN_CONTACT_READ_004: should view all contacts regardless of creator', () => {
|
|
135
|
+
contacts.list.validatePageVisible()
|
|
136
|
+
contacts.list.validateTableVisible()
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
describe('UPDATE - Admin can update contacts', () => {
|
|
141
|
+
it('ADMIN_CONTACT_UPDATE_001: should edit contact successfully', () => {
|
|
142
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
143
|
+
if ($rows.length > 0) {
|
|
144
|
+
contacts.list.clickRowByIndex(0)
|
|
145
|
+
|
|
146
|
+
cy.url().then(url => {
|
|
147
|
+
const contactId = url.split('/').pop()
|
|
148
|
+
contacts.form.visitEdit(contactId!)
|
|
149
|
+
contacts.form.validateFormVisible()
|
|
150
|
+
|
|
151
|
+
const updatedFirstName = `UpdatedAdmin${Date.now()}`
|
|
152
|
+
contacts.form.typeInField('firstName', updatedFirstName)
|
|
153
|
+
|
|
154
|
+
contacts.submitForm()
|
|
155
|
+
|
|
156
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
157
|
+
cy.contains(updatedFirstName).should('be.visible')
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('ADMIN_CONTACT_UPDATE_002: should update contact work info', () => {
|
|
164
|
+
const firstName = `WorkUpdate${Date.now()}`
|
|
165
|
+
const lastName = 'Admin'
|
|
166
|
+
const email = `work.admin.${Date.now()}@test.com`
|
|
167
|
+
|
|
168
|
+
contacts.list.clickCreate()
|
|
169
|
+
contacts.fillContactForm({ firstName, lastName, email })
|
|
170
|
+
contacts.submitForm()
|
|
171
|
+
|
|
172
|
+
cy.contains(firstName).should('be.visible')
|
|
173
|
+
|
|
174
|
+
// Edit the contact
|
|
175
|
+
cy.get(contacts.list.selectors.rowGeneric).first().within(() => {
|
|
176
|
+
cy.get('[data-cy*="edit"]').click()
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
contacts.form.validateFormVisible()
|
|
180
|
+
|
|
181
|
+
contacts.form.typeInField('title', 'VP of Sales')
|
|
182
|
+
contacts.form.typeInField('phone', '+34 91 888 7777')
|
|
183
|
+
|
|
184
|
+
contacts.submitForm()
|
|
185
|
+
|
|
186
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('ADMIN_CONTACT_UPDATE_003: should cancel update without saving', () => {
|
|
190
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
191
|
+
if ($rows.length > 0) {
|
|
192
|
+
contacts.list.clickRowByIndex(0)
|
|
193
|
+
|
|
194
|
+
cy.url().then(url => {
|
|
195
|
+
const contactId = url.split('/').pop()
|
|
196
|
+
contacts.form.visitEdit(contactId!)
|
|
197
|
+
contacts.form.validateFormVisible()
|
|
198
|
+
|
|
199
|
+
contacts.form.typeInField('firstName', 'ShouldNotSave')
|
|
200
|
+
|
|
201
|
+
contacts.form.cancel()
|
|
202
|
+
cy.url().should('not.include', '/edit')
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('DELETE - Admin can delete contacts', () => {
|
|
210
|
+
it('ADMIN_CONTACT_DELETE_001: should delete contact successfully', () => {
|
|
211
|
+
const firstName = `DeleteTest${Date.now()}`
|
|
212
|
+
const lastName = 'Admin'
|
|
213
|
+
const email = `delete.admin.${Date.now()}@test.com`
|
|
214
|
+
|
|
215
|
+
contacts.list.clickCreate()
|
|
216
|
+
contacts.fillContactForm({ firstName, lastName, email })
|
|
217
|
+
contacts.submitForm()
|
|
218
|
+
|
|
219
|
+
cy.contains(firstName).should('be.visible')
|
|
220
|
+
|
|
221
|
+
contacts.list.search(firstName)
|
|
222
|
+
|
|
223
|
+
cy.get(contacts.list.selectors.rowGeneric).first().within(() => {
|
|
224
|
+
cy.get('[data-cy*="delete"]').click()
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
contacts.list.confirmDelete()
|
|
228
|
+
|
|
229
|
+
cy.wait(1000)
|
|
230
|
+
contacts.list.search(firstName)
|
|
231
|
+
cy.get(contacts.list.selectors.emptyState).should('be.visible')
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('ADMIN_CONTACT_DELETE_002: should cancel deletion', () => {
|
|
235
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
236
|
+
if ($rows.length > 0) {
|
|
237
|
+
cy.get(contacts.list.selectors.rowGeneric).first().within(() => {
|
|
238
|
+
cy.get('[data-cy*="delete"]').click()
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
contacts.list.cancelDelete()
|
|
242
|
+
contacts.list.validateTableVisible()
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('ADMIN_CONTACT_DELETE_003: should handle bulk delete', () => {
|
|
248
|
+
contacts.list.selectAll()
|
|
249
|
+
contacts.list.validateBulkActionsVisible()
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
})
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Contacts CRUD - Member Role (Create + Read + Update, No Delete)
|
|
5
|
+
*
|
|
6
|
+
* Uses CRM theme-specific POMs with Entity Testing Convention.
|
|
7
|
+
* Selectors follow the pattern: {slug}-{component}-{detail}
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ContactsPOM } from '../../../src/entities/ContactsPOM'
|
|
11
|
+
import { loginAsCrmMember } from '../../../src/session-helpers'
|
|
12
|
+
|
|
13
|
+
describe('Contacts CRUD - Member Role (Create + Read + Update)', () => {
|
|
14
|
+
const contacts = new ContactsPOM()
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
loginAsCrmMember()
|
|
18
|
+
cy.visit('/dashboard/contacts')
|
|
19
|
+
contacts.list.validatePageVisible()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('CREATE - Member CAN create contacts', () => {
|
|
23
|
+
it('MEMBER_CONTACT_CREATE_001: should show add button', () => {
|
|
24
|
+
cy.get(contacts.list.selectors.createButton).should('be.visible')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('MEMBER_CONTACT_CREATE_002: should create new contact successfully', () => {
|
|
28
|
+
contacts.list.clickCreate()
|
|
29
|
+
contacts.form.validateFormVisible()
|
|
30
|
+
|
|
31
|
+
const timestamp = Date.now()
|
|
32
|
+
|
|
33
|
+
contacts.fillContactForm({
|
|
34
|
+
firstName: `MemberContact${timestamp}`,
|
|
35
|
+
lastName: 'Test',
|
|
36
|
+
email: `membercontact${timestamp}@test.com`,
|
|
37
|
+
phone: '+34 600 123 456',
|
|
38
|
+
title: 'Sales Manager'
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
contacts.submitForm()
|
|
42
|
+
|
|
43
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
44
|
+
cy.contains(`MemberContact${timestamp}`).should('be.visible')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('MEMBER_CONTACT_CREATE_003: should create contact with minimal required fields', () => {
|
|
48
|
+
contacts.list.clickCreate()
|
|
49
|
+
contacts.form.validateFormVisible()
|
|
50
|
+
|
|
51
|
+
const timestamp = Date.now()
|
|
52
|
+
contacts.fillContactForm({
|
|
53
|
+
firstName: `MinimalMemberContact${timestamp}`,
|
|
54
|
+
email: `minimal${timestamp}@test.com`
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
contacts.submitForm()
|
|
58
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('READ - Member can read contacts', () => {
|
|
63
|
+
it('MEMBER_CONTACT_READ_001: should view contact list', () => {
|
|
64
|
+
contacts.list.validatePageVisible()
|
|
65
|
+
contacts.list.validateTableVisible()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('MEMBER_CONTACT_READ_002: should view contact details', () => {
|
|
69
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
70
|
+
if ($rows.length > 0) {
|
|
71
|
+
contacts.list.clickRowByIndex(0)
|
|
72
|
+
cy.url().should('match', /\/dashboard\/contacts\/[a-z0-9-]+/)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('MEMBER_CONTACT_READ_003: should search contacts', () => {
|
|
78
|
+
contacts.list.search('test')
|
|
79
|
+
cy.wait(500)
|
|
80
|
+
contacts.list.clearSearch()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('MEMBER_CONTACT_READ_004: should view all contacts in organization', () => {
|
|
84
|
+
contacts.list.validatePageVisible()
|
|
85
|
+
|
|
86
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
87
|
+
if ($rows.length > 0) {
|
|
88
|
+
cy.get(contacts.list.selectors.rowGeneric).should('have.length.at.least', 1)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe('UPDATE - Member CAN update contacts', () => {
|
|
95
|
+
it('MEMBER_CONTACT_UPDATE_001: should edit contact successfully', () => {
|
|
96
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
97
|
+
if ($rows.length > 0) {
|
|
98
|
+
contacts.list.clickRowByIndex(0)
|
|
99
|
+
|
|
100
|
+
cy.url().then(url => {
|
|
101
|
+
const contactId = url.split('/').pop()
|
|
102
|
+
contacts.form.visitEdit(contactId!)
|
|
103
|
+
contacts.form.validateFormVisible()
|
|
104
|
+
|
|
105
|
+
const updatedFirstName = `UpdatedMember${Date.now()}`
|
|
106
|
+
contacts.form.typeInField('firstName', updatedFirstName)
|
|
107
|
+
|
|
108
|
+
contacts.submitForm()
|
|
109
|
+
|
|
110
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
111
|
+
cy.contains(updatedFirstName).should('be.visible')
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('MEMBER_CONTACT_UPDATE_002: should update contact details', () => {
|
|
118
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
119
|
+
if ($rows.length > 0) {
|
|
120
|
+
contacts.list.clickRowByIndex(0)
|
|
121
|
+
|
|
122
|
+
cy.url().then(url => {
|
|
123
|
+
const contactId = url.split('/').pop()
|
|
124
|
+
contacts.form.visitEdit(contactId!)
|
|
125
|
+
contacts.form.validateFormVisible()
|
|
126
|
+
|
|
127
|
+
const timestamp = Date.now()
|
|
128
|
+
contacts.form.typeInField('firstName', `Miguel${timestamp}`)
|
|
129
|
+
contacts.form.typeInField('title', 'Sales Representative')
|
|
130
|
+
contacts.form.typeInField('phone', '+34 600 111 222')
|
|
131
|
+
|
|
132
|
+
contacts.submitForm()
|
|
133
|
+
|
|
134
|
+
cy.url().should('include', '/dashboard/contacts')
|
|
135
|
+
cy.contains(`Miguel${timestamp}`).should('be.visible')
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('MEMBER_CONTACT_UPDATE_003: should cancel update without saving', () => {
|
|
142
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
143
|
+
if ($rows.length > 0) {
|
|
144
|
+
contacts.list.clickRowByIndex(0)
|
|
145
|
+
|
|
146
|
+
cy.url().then(url => {
|
|
147
|
+
const contactId = url.split('/').pop()
|
|
148
|
+
contacts.form.visitEdit(contactId!)
|
|
149
|
+
contacts.form.validateFormVisible()
|
|
150
|
+
|
|
151
|
+
contacts.form.typeInField('firstName', 'ShouldNotSave')
|
|
152
|
+
|
|
153
|
+
contacts.form.cancel()
|
|
154
|
+
cy.url().should('not.include', '/edit')
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
describe('DELETE - Member CANNOT delete contacts', () => {
|
|
162
|
+
it('MEMBER_CONTACT_DELETE_001: should NOT show delete button', () => {
|
|
163
|
+
cy.get('[data-cy*="delete"]').should('not.exist')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('MEMBER_CONTACT_DELETE_002: should NOT delete contact via API', () => {
|
|
167
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
168
|
+
if ($rows.length > 0) {
|
|
169
|
+
cy.get(contacts.list.selectors.rowGeneric).first().invoke('attr', 'data-cy').then(dataCy => {
|
|
170
|
+
if (dataCy) {
|
|
171
|
+
const contactId = dataCy.replace('contacts-row-', '')
|
|
172
|
+
|
|
173
|
+
cy.request({
|
|
174
|
+
method: 'DELETE',
|
|
175
|
+
url: `/api/v1/contacts/${contactId}`,
|
|
176
|
+
failOnStatusCode: false,
|
|
177
|
+
}).then(response => {
|
|
178
|
+
expect(response.status).to.be.oneOf([401, 403, 404])
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
describe('PERMISSIONS - Member role capabilities', () => {
|
|
188
|
+
it('MEMBER_CONTACT_PERMISSIONS_001: should have create, read, and update access', () => {
|
|
189
|
+
contacts.list.validatePageVisible()
|
|
190
|
+
|
|
191
|
+
// Check for add button (create permission)
|
|
192
|
+
cy.get(contacts.list.selectors.createButton).should('be.visible')
|
|
193
|
+
|
|
194
|
+
// Check for no delete buttons (no delete permission)
|
|
195
|
+
cy.get('[data-cy*="delete"]').should('not.exist')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('MEMBER_CONTACT_PERMISSIONS_002: should see all organization contacts', () => {
|
|
199
|
+
contacts.list.validatePageVisible()
|
|
200
|
+
|
|
201
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
202
|
+
if ($rows.length > 0) {
|
|
203
|
+
cy.get(contacts.list.selectors.rowGeneric).should('have.length.at.least', 1)
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('MEMBER_CONTACT_PERMISSIONS_003: should create and update any contact', () => {
|
|
209
|
+
cy.get(contacts.list.selectors.createButton).should('be.visible')
|
|
210
|
+
|
|
211
|
+
cy.get(contacts.list.selectors.rowGeneric).then($rows => {
|
|
212
|
+
if ($rows.length > 0) {
|
|
213
|
+
// Verify edit capability
|
|
214
|
+
contacts.list.clickRowByIndex(0)
|
|
215
|
+
cy.url().then(url => {
|
|
216
|
+
const contactId = url.split('/').pop()
|
|
217
|
+
contacts.form.visitEdit(contactId!)
|
|
218
|
+
contacts.form.validateFormVisible()
|
|
219
|
+
})
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
})
|