@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,240 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./entities.schema.json",
|
|
3
|
+
"_warning": "AUTO-GENERATED by build-registry.mjs - DO NOT EDIT MANUALLY",
|
|
4
|
+
"_theme": "crm",
|
|
5
|
+
"entities": {
|
|
6
|
+
"activities": {
|
|
7
|
+
"slug": "activities",
|
|
8
|
+
"singular": "activity",
|
|
9
|
+
"plural": "Activities",
|
|
10
|
+
"tableName": "activities",
|
|
11
|
+
"fields": [
|
|
12
|
+
"type",
|
|
13
|
+
"subject",
|
|
14
|
+
"description",
|
|
15
|
+
"status",
|
|
16
|
+
"priority",
|
|
17
|
+
"dueDate",
|
|
18
|
+
"completedAt",
|
|
19
|
+
"duration",
|
|
20
|
+
"outcome",
|
|
21
|
+
"location",
|
|
22
|
+
"contactId",
|
|
23
|
+
"companyId",
|
|
24
|
+
"opportunityId",
|
|
25
|
+
"assignedTo",
|
|
26
|
+
"createdAt",
|
|
27
|
+
"updatedAt"
|
|
28
|
+
],
|
|
29
|
+
"filters": [],
|
|
30
|
+
"source": "theme"
|
|
31
|
+
},
|
|
32
|
+
"campaigns": {
|
|
33
|
+
"slug": "campaigns",
|
|
34
|
+
"singular": "campaign",
|
|
35
|
+
"plural": "Campaigns",
|
|
36
|
+
"tableName": "campaigns",
|
|
37
|
+
"fields": [
|
|
38
|
+
"name",
|
|
39
|
+
"type",
|
|
40
|
+
"status",
|
|
41
|
+
"objective",
|
|
42
|
+
"description",
|
|
43
|
+
"startDate",
|
|
44
|
+
"endDate",
|
|
45
|
+
"budget",
|
|
46
|
+
"actualCost",
|
|
47
|
+
"targetAudience",
|
|
48
|
+
"targetLeads",
|
|
49
|
+
"actualLeads",
|
|
50
|
+
"targetRevenue",
|
|
51
|
+
"actualRevenue",
|
|
52
|
+
"roi",
|
|
53
|
+
"channel",
|
|
54
|
+
"assignedTo",
|
|
55
|
+
"createdAt",
|
|
56
|
+
"updatedAt"
|
|
57
|
+
],
|
|
58
|
+
"filters": [],
|
|
59
|
+
"source": "theme"
|
|
60
|
+
},
|
|
61
|
+
"companies": {
|
|
62
|
+
"slug": "companies",
|
|
63
|
+
"singular": "company",
|
|
64
|
+
"plural": "Companies",
|
|
65
|
+
"tableName": "companies",
|
|
66
|
+
"fields": [
|
|
67
|
+
"name",
|
|
68
|
+
"legalName",
|
|
69
|
+
"taxId",
|
|
70
|
+
"website",
|
|
71
|
+
"email",
|
|
72
|
+
"phone",
|
|
73
|
+
"industry",
|
|
74
|
+
"type",
|
|
75
|
+
"size",
|
|
76
|
+
"annualRevenue",
|
|
77
|
+
"address",
|
|
78
|
+
"city",
|
|
79
|
+
"state",
|
|
80
|
+
"country",
|
|
81
|
+
"postalCode",
|
|
82
|
+
"logo",
|
|
83
|
+
"rating",
|
|
84
|
+
"assignedTo",
|
|
85
|
+
"createdAt",
|
|
86
|
+
"updatedAt"
|
|
87
|
+
],
|
|
88
|
+
"filters": [],
|
|
89
|
+
"source": "theme"
|
|
90
|
+
},
|
|
91
|
+
"contacts": {
|
|
92
|
+
"slug": "contacts",
|
|
93
|
+
"singular": "contact",
|
|
94
|
+
"plural": "Contacts",
|
|
95
|
+
"tableName": "contacts",
|
|
96
|
+
"fields": [
|
|
97
|
+
"firstName",
|
|
98
|
+
"lastName",
|
|
99
|
+
"email",
|
|
100
|
+
"phone",
|
|
101
|
+
"mobile",
|
|
102
|
+
"companyId",
|
|
103
|
+
"position",
|
|
104
|
+
"department",
|
|
105
|
+
"isPrimary",
|
|
106
|
+
"birthDate",
|
|
107
|
+
"linkedin",
|
|
108
|
+
"twitter",
|
|
109
|
+
"preferredChannel",
|
|
110
|
+
"timezone",
|
|
111
|
+
"lastContactedAt",
|
|
112
|
+
"createdAt",
|
|
113
|
+
"updatedAt"
|
|
114
|
+
],
|
|
115
|
+
"filters": [],
|
|
116
|
+
"source": "theme"
|
|
117
|
+
},
|
|
118
|
+
"leads": {
|
|
119
|
+
"slug": "leads",
|
|
120
|
+
"singular": "lead",
|
|
121
|
+
"plural": "Leads",
|
|
122
|
+
"tableName": "leads",
|
|
123
|
+
"fields": [
|
|
124
|
+
"companyName",
|
|
125
|
+
"contactName",
|
|
126
|
+
"email",
|
|
127
|
+
"phone",
|
|
128
|
+
"website",
|
|
129
|
+
"source",
|
|
130
|
+
"status",
|
|
131
|
+
"score",
|
|
132
|
+
"industry",
|
|
133
|
+
"companySize",
|
|
134
|
+
"budget",
|
|
135
|
+
"assignedTo",
|
|
136
|
+
"notes",
|
|
137
|
+
"createdAt",
|
|
138
|
+
"updatedAt"
|
|
139
|
+
],
|
|
140
|
+
"filters": [],
|
|
141
|
+
"source": "theme"
|
|
142
|
+
},
|
|
143
|
+
"notes": {
|
|
144
|
+
"slug": "notes",
|
|
145
|
+
"singular": "note",
|
|
146
|
+
"plural": "Notes",
|
|
147
|
+
"tableName": "notes",
|
|
148
|
+
"fields": [
|
|
149
|
+
"title",
|
|
150
|
+
"content",
|
|
151
|
+
"type",
|
|
152
|
+
"isPinned",
|
|
153
|
+
"isPrivate",
|
|
154
|
+
"entityType",
|
|
155
|
+
"entityId",
|
|
156
|
+
"contactId",
|
|
157
|
+
"companyId",
|
|
158
|
+
"opportunityId",
|
|
159
|
+
"attachments",
|
|
160
|
+
"createdAt",
|
|
161
|
+
"updatedAt"
|
|
162
|
+
],
|
|
163
|
+
"filters": [],
|
|
164
|
+
"source": "theme"
|
|
165
|
+
},
|
|
166
|
+
"opportunities": {
|
|
167
|
+
"slug": "opportunities",
|
|
168
|
+
"singular": "opportunity",
|
|
169
|
+
"plural": "Opportunities",
|
|
170
|
+
"tableName": "opportunities",
|
|
171
|
+
"fields": [
|
|
172
|
+
"name",
|
|
173
|
+
"companyId",
|
|
174
|
+
"contactId",
|
|
175
|
+
"pipelineId",
|
|
176
|
+
"stageId",
|
|
177
|
+
"amount",
|
|
178
|
+
"currency",
|
|
179
|
+
"probability",
|
|
180
|
+
"expectedRevenue",
|
|
181
|
+
"closeDate",
|
|
182
|
+
"type",
|
|
183
|
+
"source",
|
|
184
|
+
"competitor",
|
|
185
|
+
"status",
|
|
186
|
+
"lostReason",
|
|
187
|
+
"assignedTo",
|
|
188
|
+
"createdAt",
|
|
189
|
+
"updatedAt"
|
|
190
|
+
],
|
|
191
|
+
"filters": [],
|
|
192
|
+
"source": "theme"
|
|
193
|
+
},
|
|
194
|
+
"pipelines": {
|
|
195
|
+
"slug": "pipelines",
|
|
196
|
+
"singular": "pipeline",
|
|
197
|
+
"plural": "Pipelines",
|
|
198
|
+
"tableName": "pipelines",
|
|
199
|
+
"fields": [
|
|
200
|
+
"name",
|
|
201
|
+
"description",
|
|
202
|
+
"type",
|
|
203
|
+
"isDefault",
|
|
204
|
+
"isActive",
|
|
205
|
+
"stages",
|
|
206
|
+
"dealRottenDays",
|
|
207
|
+
"createdAt",
|
|
208
|
+
"updatedAt"
|
|
209
|
+
],
|
|
210
|
+
"filters": [],
|
|
211
|
+
"source": "theme"
|
|
212
|
+
},
|
|
213
|
+
"products": {
|
|
214
|
+
"slug": "products",
|
|
215
|
+
"singular": "product",
|
|
216
|
+
"plural": "Products",
|
|
217
|
+
"tableName": "products",
|
|
218
|
+
"fields": [
|
|
219
|
+
"code",
|
|
220
|
+
"name",
|
|
221
|
+
"category",
|
|
222
|
+
"type",
|
|
223
|
+
"description",
|
|
224
|
+
"price",
|
|
225
|
+
"cost",
|
|
226
|
+
"currency",
|
|
227
|
+
"unit",
|
|
228
|
+
"isActive",
|
|
229
|
+
"minimumQuantity",
|
|
230
|
+
"image",
|
|
231
|
+
"brochureUrl",
|
|
232
|
+
"commissionRate",
|
|
233
|
+
"createdAt",
|
|
234
|
+
"updatedAt"
|
|
235
|
+
],
|
|
236
|
+
"filters": [],
|
|
237
|
+
"source": "theme"
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRMDataTable - Page Object Model Class
|
|
3
|
+
*
|
|
4
|
+
* POM for the CRM theme data table component.
|
|
5
|
+
* Handles data table interactions for entity lists (leads, contacts, companies, etc).
|
|
6
|
+
*
|
|
7
|
+
* Supports both entity-specific selectors (via constructor) and generic selectors.
|
|
8
|
+
* When entitySlug is provided, uses `{entitySlug}-*` selectors.
|
|
9
|
+
* Otherwise, uses generic `crm-datatable-*` selectors.
|
|
10
|
+
*/
|
|
11
|
+
export class CRMDataTable {
|
|
12
|
+
// Generic fallback selectors (when no entitySlug provided)
|
|
13
|
+
static selectors = {
|
|
14
|
+
container: '[data-cy="crm-datatable-list"]',
|
|
15
|
+
search: '[data-cy="crm-datatable-search"]',
|
|
16
|
+
table: '[data-cy="crm-datatable-table"]',
|
|
17
|
+
tableRow: '[data-cy^="crm-datatable-row-"]',
|
|
18
|
+
emptyState: '[data-cy="crm-datatable-empty"]',
|
|
19
|
+
pagination: '[data-cy="crm-datatable-pagination"]',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get entity-specific selectors
|
|
24
|
+
* @param {string} entitySlug - The entity slug (e.g., 'leads', 'contacts')
|
|
25
|
+
*/
|
|
26
|
+
static getEntitySelectors(entitySlug) {
|
|
27
|
+
return {
|
|
28
|
+
container: `[data-cy="${entitySlug}-list"]`,
|
|
29
|
+
addButton: `[data-cy="${entitySlug}-add"]`,
|
|
30
|
+
search: `[data-cy="${entitySlug}-search"]`,
|
|
31
|
+
table: `[data-cy="${entitySlug}-table"]`,
|
|
32
|
+
tableRow: `[data-cy^="${entitySlug}-row-"]`,
|
|
33
|
+
emptyState: `[data-cy="${entitySlug}-empty"]`,
|
|
34
|
+
pagination: `[data-cy="${entitySlug}-pagination"]`,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} [entitySlug] - Optional entity slug for entity-specific selectors
|
|
40
|
+
*/
|
|
41
|
+
constructor(entitySlug = null) {
|
|
42
|
+
this.entitySlug = entitySlug
|
|
43
|
+
this.selectors = entitySlug
|
|
44
|
+
? CRMDataTable.getEntitySelectors(entitySlug)
|
|
45
|
+
: CRMDataTable.selectors
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate data table container is visible
|
|
50
|
+
*/
|
|
51
|
+
validateVisible() {
|
|
52
|
+
cy.get(this.selectors.container).should('be.visible')
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Validate table is visible
|
|
58
|
+
*/
|
|
59
|
+
validateTableVisible() {
|
|
60
|
+
cy.get(this.selectors.table).should('be.visible')
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Click the add button to create a new entity
|
|
66
|
+
*/
|
|
67
|
+
clickAdd() {
|
|
68
|
+
if (!this.entitySlug) {
|
|
69
|
+
throw new Error('clickAdd() requires entitySlug to be set in constructor')
|
|
70
|
+
}
|
|
71
|
+
cy.get(this.selectors.addButton).click()
|
|
72
|
+
return this
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Validate add button is visible
|
|
77
|
+
*/
|
|
78
|
+
validateAddButtonVisible() {
|
|
79
|
+
if (!this.entitySlug) {
|
|
80
|
+
throw new Error('validateAddButtonVisible() requires entitySlug to be set in constructor')
|
|
81
|
+
}
|
|
82
|
+
cy.get(this.selectors.addButton).should('be.visible')
|
|
83
|
+
return this
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validate add button is not visible (e.g., for restricted roles)
|
|
88
|
+
*/
|
|
89
|
+
validateAddButtonNotVisible() {
|
|
90
|
+
if (!this.entitySlug) {
|
|
91
|
+
throw new Error('validateAddButtonNotVisible() requires entitySlug to be set in constructor')
|
|
92
|
+
}
|
|
93
|
+
cy.get(this.selectors.addButton).should('not.exist')
|
|
94
|
+
return this
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Search for a term
|
|
99
|
+
* @param {string} term - Search term
|
|
100
|
+
*/
|
|
101
|
+
search(term) {
|
|
102
|
+
cy.get(this.selectors.search).clear().type(term)
|
|
103
|
+
// Wait for debounce
|
|
104
|
+
cy.wait(300)
|
|
105
|
+
return this
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Clear search input
|
|
110
|
+
*/
|
|
111
|
+
clearSearch() {
|
|
112
|
+
cy.get(this.selectors.search).clear()
|
|
113
|
+
return this
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Validate search input has value
|
|
118
|
+
* @param {string} value - Expected value
|
|
119
|
+
*/
|
|
120
|
+
validateSearchValue(value) {
|
|
121
|
+
cy.get(this.selectors.search).should('have.value', value)
|
|
122
|
+
return this
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get a specific row by ID
|
|
127
|
+
* @param {string} id - Row ID
|
|
128
|
+
*/
|
|
129
|
+
getRow(id) {
|
|
130
|
+
const prefix = this.entitySlug || 'crm-datatable'
|
|
131
|
+
return cy.get(`[data-cy="${prefix}-row-${id}"]`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Click a specific row
|
|
136
|
+
* @param {string} id - Row ID
|
|
137
|
+
*/
|
|
138
|
+
clickRow(id) {
|
|
139
|
+
this.getRow(id).click()
|
|
140
|
+
return this
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Validate row exists
|
|
145
|
+
* @param {string} id - Row ID
|
|
146
|
+
*/
|
|
147
|
+
validateRowExists(id) {
|
|
148
|
+
this.getRow(id).should('exist')
|
|
149
|
+
return this
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Validate row does not exist
|
|
154
|
+
* @param {string} id - Row ID
|
|
155
|
+
*/
|
|
156
|
+
validateRowNotExists(id) {
|
|
157
|
+
const prefix = this.entitySlug || 'crm-datatable'
|
|
158
|
+
cy.get(`[data-cy="${prefix}-row-${id}"]`).should('not.exist')
|
|
159
|
+
return this
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Validate number of visible rows
|
|
164
|
+
* @param {number} count - Expected row count
|
|
165
|
+
*/
|
|
166
|
+
validateRowCount(count) {
|
|
167
|
+
if (count === 0) {
|
|
168
|
+
this.validateEmpty()
|
|
169
|
+
} else {
|
|
170
|
+
cy.get(this.selectors.tableRow).should('have.length', count)
|
|
171
|
+
}
|
|
172
|
+
return this
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get all visible rows
|
|
177
|
+
*/
|
|
178
|
+
getRows() {
|
|
179
|
+
return cy.get(this.selectors.tableRow)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Validate empty state is visible
|
|
184
|
+
*/
|
|
185
|
+
validateEmpty() {
|
|
186
|
+
cy.get(this.selectors.emptyState).should('be.visible')
|
|
187
|
+
return this
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Validate empty state contains message
|
|
192
|
+
* @param {string} message - Expected message
|
|
193
|
+
*/
|
|
194
|
+
validateEmptyMessage(message) {
|
|
195
|
+
cy.get(this.selectors.emptyState).should('contain', message)
|
|
196
|
+
return this
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Validate table is not empty
|
|
201
|
+
*/
|
|
202
|
+
validateNotEmpty() {
|
|
203
|
+
cy.get(this.selectors.emptyState).should('not.exist')
|
|
204
|
+
cy.get(this.selectors.tableRow).should('have.length.greaterThan', 0)
|
|
205
|
+
return this
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Validate pagination is visible
|
|
210
|
+
*/
|
|
211
|
+
validatePaginationVisible() {
|
|
212
|
+
cy.get(this.selectors.pagination).should('be.visible')
|
|
213
|
+
return this
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Validate pagination is not visible
|
|
218
|
+
*/
|
|
219
|
+
validatePaginationNotVisible() {
|
|
220
|
+
cy.get(this.selectors.pagination).should('not.exist')
|
|
221
|
+
return this
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRMMobileNav - Page Object Model Class
|
|
3
|
+
*
|
|
4
|
+
* POM for the CRM theme mobile navigation.
|
|
5
|
+
* Handles mobile topbar and bottom navigation interactions.
|
|
6
|
+
*/
|
|
7
|
+
export class CRMMobileNav {
|
|
8
|
+
static selectors = {
|
|
9
|
+
topbar: '[data-cy="crm-mobile-topbar"]',
|
|
10
|
+
bottomNav: '[data-cy="crm-mobile-bottomnav"]',
|
|
11
|
+
navDashboard: '[data-cy="crm-mobile-nav-dashboard"]',
|
|
12
|
+
navLeads: '[data-cy="crm-mobile-nav-leads"]',
|
|
13
|
+
navPipelines: '[data-cy="crm-mobile-nav-pipelines"]',
|
|
14
|
+
navActivities: '[data-cy="crm-mobile-nav-activities"]',
|
|
15
|
+
moreBtn: '[data-cy="crm-mobile-nav-more"]',
|
|
16
|
+
moreSheet: '[data-cy="crm-mobile-more-sheet"]',
|
|
17
|
+
moreSheetContent: '[data-cy="crm-mobile-more-sheet-content"]',
|
|
18
|
+
moreContacts: '[data-cy="crm-more-nav-contacts"]',
|
|
19
|
+
moreCompanies: '[data-cy="crm-more-nav-companies"]',
|
|
20
|
+
moreProducts: '[data-cy="crm-more-nav-products"]',
|
|
21
|
+
moreCampaigns: '[data-cy="crm-more-nav-campaigns"]',
|
|
22
|
+
moreSettings: '[data-cy="crm-more-nav-settings"]',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Validate mobile topbar is visible
|
|
27
|
+
*/
|
|
28
|
+
validateTopbarVisible() {
|
|
29
|
+
cy.get(CRMMobileNav.selectors.topbar).should('be.visible')
|
|
30
|
+
return this
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Validate mobile bottom nav is visible
|
|
35
|
+
*/
|
|
36
|
+
validateBottomNavVisible() {
|
|
37
|
+
cy.get(CRMMobileNav.selectors.bottomNav).should('be.visible')
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validate mobile navigation is visible
|
|
43
|
+
*/
|
|
44
|
+
validateVisible() {
|
|
45
|
+
this.validateTopbarVisible()
|
|
46
|
+
this.validateBottomNavVisible()
|
|
47
|
+
return this
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Navigate to an entity via bottom nav
|
|
52
|
+
* @param {string} entity - Entity name (dashboard, leads, pipelines, activities)
|
|
53
|
+
*/
|
|
54
|
+
navigateTo(entity) {
|
|
55
|
+
const selector = CRMMobileNav.selectors[`nav${entity.charAt(0).toUpperCase() + entity.slice(1)}`]
|
|
56
|
+
if (!selector) {
|
|
57
|
+
throw new Error(`Invalid entity: ${entity}. Valid options: dashboard, leads, pipelines, activities. For other entities, use navigateToMore().`)
|
|
58
|
+
}
|
|
59
|
+
cy.get(selector).click()
|
|
60
|
+
return this
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Open the more sheet
|
|
65
|
+
*/
|
|
66
|
+
openMore() {
|
|
67
|
+
cy.get(CRMMobileNav.selectors.moreBtn).click()
|
|
68
|
+
cy.get(CRMMobileNav.selectors.moreSheetContent).should('be.visible')
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Close the more sheet
|
|
74
|
+
*/
|
|
75
|
+
closeMore() {
|
|
76
|
+
cy.get('body').click(0, 0)
|
|
77
|
+
cy.get(CRMMobileNav.selectors.moreSheetContent).should('not.be.visible')
|
|
78
|
+
return this
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Validate more sheet is visible
|
|
83
|
+
*/
|
|
84
|
+
validateMoreSheetVisible() {
|
|
85
|
+
cy.get(CRMMobileNav.selectors.moreSheetContent).should('be.visible')
|
|
86
|
+
return this
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Navigate to an entity from the more sheet
|
|
91
|
+
* @param {string} entity - Entity name (contacts, companies, products, campaigns, settings)
|
|
92
|
+
*/
|
|
93
|
+
navigateToMore(entity) {
|
|
94
|
+
this.openMore()
|
|
95
|
+
const selector = CRMMobileNav.selectors[`more${entity.charAt(0).toUpperCase() + entity.slice(1)}`]
|
|
96
|
+
if (!selector) {
|
|
97
|
+
throw new Error(`Invalid entity: ${entity}. Valid options: contacts, companies, products, campaigns, settings`)
|
|
98
|
+
}
|
|
99
|
+
cy.get(selector).click()
|
|
100
|
+
return this
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Validate navigation item is active
|
|
105
|
+
* @param {string} entity - Entity name
|
|
106
|
+
*/
|
|
107
|
+
validateNavActive(entity) {
|
|
108
|
+
const selector = CRMMobileNav.selectors[`nav${entity.charAt(0).toUpperCase() + entity.slice(1)}`]
|
|
109
|
+
if (selector) {
|
|
110
|
+
cy.get(selector).should('have.attr', 'data-active', 'true')
|
|
111
|
+
}
|
|
112
|
+
return this
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Validate navigation item is not active
|
|
117
|
+
* @param {string} entity - Entity name
|
|
118
|
+
*/
|
|
119
|
+
validateNavNotActive(entity) {
|
|
120
|
+
const selector = CRMMobileNav.selectors[`nav${entity.charAt(0).toUpperCase() + entity.slice(1)}`]
|
|
121
|
+
if (selector) {
|
|
122
|
+
cy.get(selector).should('have.attr', 'data-active', 'false')
|
|
123
|
+
}
|
|
124
|
+
return this
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Validate all bottom nav items are visible
|
|
129
|
+
*/
|
|
130
|
+
validateAllNavItemsVisible() {
|
|
131
|
+
cy.get(CRMMobileNav.selectors.navDashboard).should('be.visible')
|
|
132
|
+
cy.get(CRMMobileNav.selectors.navLeads).should('be.visible')
|
|
133
|
+
cy.get(CRMMobileNav.selectors.navPipelines).should('be.visible')
|
|
134
|
+
cy.get(CRMMobileNav.selectors.navActivities).should('be.visible')
|
|
135
|
+
cy.get(CRMMobileNav.selectors.moreBtn).should('be.visible')
|
|
136
|
+
return this
|
|
137
|
+
}
|
|
138
|
+
}
|