@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,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PipelineAPIController - TypeScript controller for Pipelines API
|
|
3
|
+
*
|
|
4
|
+
* Handles CRUD operations for /api/v1/pipelines endpoints
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { BaseAPIController, APIRequestOptions, APIResponse } from './BaseAPIController'
|
|
8
|
+
|
|
9
|
+
export interface PipelineStage {
|
|
10
|
+
id?: string
|
|
11
|
+
name: string
|
|
12
|
+
probability: number
|
|
13
|
+
order: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PipelineData {
|
|
17
|
+
name?: string
|
|
18
|
+
description?: string
|
|
19
|
+
stages?: PipelineStage[]
|
|
20
|
+
isDefault?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PipelineGetAllOptions extends APIRequestOptions {
|
|
24
|
+
isDefault?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class PipelineAPIController extends BaseAPIController {
|
|
28
|
+
protected entitySlug = 'pipelines'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* GET all pipelines with filtering options
|
|
32
|
+
*/
|
|
33
|
+
getAll(options: PipelineGetAllOptions = {}): Cypress.Chainable<APIResponse> {
|
|
34
|
+
return super.getAll(options)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Set pipeline as default
|
|
39
|
+
*/
|
|
40
|
+
setAsDefault(id: string, options: APIRequestOptions = {}): Cypress.Chainable<APIResponse> {
|
|
41
|
+
return this.update(id, { isDefault: true }, options)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Add stage to pipeline
|
|
46
|
+
*/
|
|
47
|
+
addStage(id: string, stage: PipelineStage, options: APIRequestOptions = {}): Cypress.Chainable<APIResponse> {
|
|
48
|
+
return this.getById(id).then((response) => {
|
|
49
|
+
if (response.status !== 200) {
|
|
50
|
+
throw new Error(`Failed to get pipeline: ${response.body?.error || 'Unknown error'}`)
|
|
51
|
+
}
|
|
52
|
+
const currentStages = (response.body.data as { stages?: PipelineStage[] }).stages || []
|
|
53
|
+
const newStages = [...currentStages, stage]
|
|
54
|
+
return this.update(id, { stages: newStages }, options)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate random pipeline data for testing
|
|
60
|
+
*/
|
|
61
|
+
generateRandomData(overrides: Partial<PipelineData> = {}): PipelineData {
|
|
62
|
+
const timestamp = Date.now()
|
|
63
|
+
const randomId = Math.random().toString(36).substring(2, 8)
|
|
64
|
+
|
|
65
|
+
const pipelineNames = [
|
|
66
|
+
'Sales Pipeline',
|
|
67
|
+
'Enterprise Sales',
|
|
68
|
+
'SMB Pipeline',
|
|
69
|
+
'Partner Pipeline',
|
|
70
|
+
'Renewal Pipeline',
|
|
71
|
+
'Upsell Pipeline'
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
const defaultStages: PipelineStage[] = [
|
|
75
|
+
{ name: 'Qualification', probability: 10, order: 1 },
|
|
76
|
+
{ name: 'Discovery', probability: 25, order: 2 },
|
|
77
|
+
{ name: 'Proposal', probability: 50, order: 3 },
|
|
78
|
+
{ name: 'Negotiation', probability: 75, order: 4 },
|
|
79
|
+
{ name: 'Closed Won', probability: 100, order: 5 }
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
name: `${pipelineNames[Math.floor(Math.random() * pipelineNames.length)]} ${randomId}`,
|
|
84
|
+
description: `Test pipeline created at ${new Date(timestamp).toISOString()}`,
|
|
85
|
+
stages: defaultStages,
|
|
86
|
+
isDefault: false,
|
|
87
|
+
...overrides
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validate pipeline object structure
|
|
93
|
+
*/
|
|
94
|
+
validateObject(pipeline: Record<string, unknown>, allowMetas = false): void {
|
|
95
|
+
this.validateSystemFields(pipeline)
|
|
96
|
+
|
|
97
|
+
expect(pipeline).to.have.property('name')
|
|
98
|
+
expect(pipeline.name).to.be.a('string')
|
|
99
|
+
|
|
100
|
+
this.validateOptionalStringFields(pipeline, ['description'])
|
|
101
|
+
|
|
102
|
+
if (pipeline.stages !== null && pipeline.stages !== undefined) {
|
|
103
|
+
expect(pipeline.stages).to.be.an('array')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (pipeline.isDefault !== null && pipeline.isDefault !== undefined) {
|
|
107
|
+
expect(pipeline.isDefault).to.be.a('boolean')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (allowMetas && Object.prototype.hasOwnProperty.call(pipeline, 'metas')) {
|
|
111
|
+
expect(pipeline.metas).to.be.an('object')
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default PipelineAPIController
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProductAPIController - TypeScript controller for Products API
|
|
3
|
+
*
|
|
4
|
+
* Handles CRUD operations for /api/v1/products endpoints
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { BaseAPIController, APIRequestOptions, APIResponse } from './BaseAPIController'
|
|
8
|
+
|
|
9
|
+
export interface ProductData {
|
|
10
|
+
name?: string
|
|
11
|
+
code?: string
|
|
12
|
+
description?: string
|
|
13
|
+
price?: number
|
|
14
|
+
currency?: string
|
|
15
|
+
category?: string
|
|
16
|
+
isActive?: boolean
|
|
17
|
+
sku?: string
|
|
18
|
+
unit?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ProductGetAllOptions extends APIRequestOptions {
|
|
22
|
+
category?: string
|
|
23
|
+
isActive?: boolean
|
|
24
|
+
minPrice?: number
|
|
25
|
+
maxPrice?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class ProductAPIController extends BaseAPIController {
|
|
29
|
+
protected entitySlug = 'products'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* GET all products with filtering options
|
|
33
|
+
*/
|
|
34
|
+
getAll(options: ProductGetAllOptions = {}): Cypress.Chainable<APIResponse> {
|
|
35
|
+
return super.getAll(options)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Activate product
|
|
40
|
+
*/
|
|
41
|
+
activate(id: string, options: APIRequestOptions = {}): Cypress.Chainable<APIResponse> {
|
|
42
|
+
return this.update(id, { isActive: true }, options)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Deactivate product
|
|
47
|
+
*/
|
|
48
|
+
deactivate(id: string, options: APIRequestOptions = {}): Cypress.Chainable<APIResponse> {
|
|
49
|
+
return this.update(id, { isActive: false }, options)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate random product data for testing
|
|
54
|
+
*/
|
|
55
|
+
generateRandomData(overrides: Partial<ProductData> = {}): ProductData {
|
|
56
|
+
const timestamp = Date.now()
|
|
57
|
+
const randomId = Math.random().toString(36).substring(2, 8)
|
|
58
|
+
|
|
59
|
+
const categories = ['Software', 'Hardware', 'Services', 'Consulting', 'Support', 'Training']
|
|
60
|
+
const currencies = ['USD', 'EUR', 'GBP']
|
|
61
|
+
const units = ['unit', 'hour', 'month', 'year', 'license']
|
|
62
|
+
|
|
63
|
+
const productNames = [
|
|
64
|
+
'Enterprise License',
|
|
65
|
+
'Professional Package',
|
|
66
|
+
'Basic Plan',
|
|
67
|
+
'Premium Support',
|
|
68
|
+
'Custom Development',
|
|
69
|
+
'Training Session'
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
name: `${productNames[Math.floor(Math.random() * productNames.length)]} ${randomId}`,
|
|
74
|
+
code: `PROD-${timestamp}-${randomId}`.toUpperCase(),
|
|
75
|
+
description: `Test product created at ${new Date(timestamp).toISOString()}`,
|
|
76
|
+
price: Math.floor(Math.random() * 10000) + 100,
|
|
77
|
+
currency: currencies[Math.floor(Math.random() * currencies.length)],
|
|
78
|
+
category: categories[Math.floor(Math.random() * categories.length)],
|
|
79
|
+
isActive: true,
|
|
80
|
+
sku: `SKU-${randomId}`.toUpperCase(),
|
|
81
|
+
unit: units[Math.floor(Math.random() * units.length)],
|
|
82
|
+
...overrides
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validate product object structure
|
|
88
|
+
*/
|
|
89
|
+
validateObject(product: Record<string, unknown>, allowMetas = false): void {
|
|
90
|
+
this.validateSystemFields(product)
|
|
91
|
+
|
|
92
|
+
expect(product).to.have.property('name')
|
|
93
|
+
expect(product.name).to.be.a('string')
|
|
94
|
+
|
|
95
|
+
this.validateOptionalStringFields(product, [
|
|
96
|
+
'code', 'description', 'currency', 'category', 'sku', 'unit'
|
|
97
|
+
])
|
|
98
|
+
|
|
99
|
+
if (product.price !== null && product.price !== undefined) {
|
|
100
|
+
expect(Number(product.price)).to.be.a('number')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (product.isActive !== null && product.isActive !== undefined) {
|
|
104
|
+
expect(product.isActive).to.be.a('boolean')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (allowMetas && Object.prototype.hasOwnProperty.call(product, 'metas')) {
|
|
108
|
+
expect(product.metas).to.be.an('object')
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default ProductAPIController
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRM API Controllers Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all TypeScript API controllers for CRM entities
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { BaseAPIController } from './BaseAPIController'
|
|
8
|
+
export type { APIRequestOptions, APIResponse, CreateTestRecordOptions } from './BaseAPIController'
|
|
9
|
+
|
|
10
|
+
export { ActivityAPIController } from './ActivityAPIController'
|
|
11
|
+
export type { ActivityData, ActivityGetAllOptions } from './ActivityAPIController'
|
|
12
|
+
|
|
13
|
+
export { LeadAPIController } from './LeadAPIController'
|
|
14
|
+
export type { LeadData, LeadGetAllOptions } from './LeadAPIController'
|
|
15
|
+
|
|
16
|
+
export { ProductAPIController } from './ProductAPIController'
|
|
17
|
+
export type { ProductData, ProductGetAllOptions } from './ProductAPIController'
|
|
18
|
+
|
|
19
|
+
export { PipelineAPIController } from './PipelineAPIController'
|
|
20
|
+
export type { PipelineData, PipelineStage, PipelineGetAllOptions } from './PipelineAPIController'
|
|
21
|
+
|
|
22
|
+
export { ContactAPIController } from './ContactAPIController'
|
|
23
|
+
export type { ContactData, ContactGetAllOptions } from './ContactAPIController'
|
|
24
|
+
|
|
25
|
+
export { CampaignAPIController } from './CampaignAPIController'
|
|
26
|
+
export type { CampaignData, CampaignGetAllOptions } from './CampaignAPIController'
|
|
27
|
+
|
|
28
|
+
export { CompanyAPIController } from './CompanyAPIController'
|
|
29
|
+
export type { CompanyData, CompanyGetAllOptions } from './CompanyAPIController'
|
|
30
|
+
|
|
31
|
+
export { OpportunityAPIController } from './OpportunityAPIController'
|
|
32
|
+
export type { OpportunityData, OpportunityGetAllOptions } from './OpportunityAPIController'
|
|
33
|
+
|
|
34
|
+
export { NoteAPIController } from './NoteAPIController'
|
|
35
|
+
export type { NoteData, NoteGetAllOptions } from './NoteAPIController'
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activities Page Object Model for CRM Theme
|
|
3
|
+
*
|
|
4
|
+
* Entity-specific POM for Activities.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const activities = new ActivitiesPOM()
|
|
8
|
+
* activities.list.validateTableVisible()
|
|
9
|
+
* activities.form.selectOption('type', 'call')
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EntityList } from '../components/EntityList'
|
|
13
|
+
import { EntityForm } from '../components/EntityForm'
|
|
14
|
+
import { EntityDetail } from '../components/EntityDetail'
|
|
15
|
+
|
|
16
|
+
export class ActivitiesPOM {
|
|
17
|
+
/** Generic list POM for activities */
|
|
18
|
+
readonly list: EntityList
|
|
19
|
+
|
|
20
|
+
/** Generic form POM for activities */
|
|
21
|
+
readonly form: EntityForm
|
|
22
|
+
|
|
23
|
+
/** Generic detail POM for activities */
|
|
24
|
+
readonly detail: EntityDetail
|
|
25
|
+
|
|
26
|
+
/** Activity entity slug */
|
|
27
|
+
readonly slug = 'activities'
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
this.list = EntityList.for('activities')
|
|
31
|
+
this.form = EntityForm.for('activities')
|
|
32
|
+
this.detail = new EntityDetail('activities', 'activity', [])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================
|
|
36
|
+
// NAVIGATION METHODS
|
|
37
|
+
// ============================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Visit the activities list page
|
|
41
|
+
*/
|
|
42
|
+
visitList() {
|
|
43
|
+
cy.visit('/dashboard/activities')
|
|
44
|
+
this.list.waitForPageLoad()
|
|
45
|
+
return this
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Visit activity detail page
|
|
50
|
+
*/
|
|
51
|
+
visitDetail(activityId: string) {
|
|
52
|
+
this.detail.visit(activityId)
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Visit create activity page
|
|
58
|
+
*/
|
|
59
|
+
visitCreate() {
|
|
60
|
+
this.form.visitCreate()
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Visit edit activity page
|
|
66
|
+
*/
|
|
67
|
+
visitEdit(activityId: string) {
|
|
68
|
+
this.form.visitEdit(activityId)
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================
|
|
73
|
+
// FORM HELPERS
|
|
74
|
+
// ============================================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Fill activity form with common fields
|
|
78
|
+
*/
|
|
79
|
+
fillActivityForm(data: {
|
|
80
|
+
type?: string
|
|
81
|
+
subject?: string
|
|
82
|
+
description?: string
|
|
83
|
+
dueDate?: string
|
|
84
|
+
relatedTo?: string
|
|
85
|
+
contactId?: string
|
|
86
|
+
companyId?: string
|
|
87
|
+
opportunityId?: string
|
|
88
|
+
}) {
|
|
89
|
+
if (data.type) {
|
|
90
|
+
this.form.selectOption('type', data.type)
|
|
91
|
+
}
|
|
92
|
+
if (data.subject) {
|
|
93
|
+
this.form.typeInField('subject', data.subject)
|
|
94
|
+
}
|
|
95
|
+
if (data.description) {
|
|
96
|
+
this.form.typeInTextarea('description', data.description)
|
|
97
|
+
}
|
|
98
|
+
if (data.dueDate) {
|
|
99
|
+
this.form.fillDate('dueDate', data.dueDate)
|
|
100
|
+
}
|
|
101
|
+
if (data.contactId) {
|
|
102
|
+
this.form.selectOption('contactId', data.contactId)
|
|
103
|
+
}
|
|
104
|
+
if (data.companyId) {
|
|
105
|
+
this.form.selectOption('companyId', data.companyId)
|
|
106
|
+
}
|
|
107
|
+
if (data.opportunityId) {
|
|
108
|
+
this.form.selectOption('opportunityId', data.opportunityId)
|
|
109
|
+
}
|
|
110
|
+
return this
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Submit the activity form
|
|
115
|
+
*/
|
|
116
|
+
submitForm() {
|
|
117
|
+
this.form.submit()
|
|
118
|
+
return this
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Mark activity as complete
|
|
123
|
+
*/
|
|
124
|
+
markComplete() {
|
|
125
|
+
cy.get('[data-cy="activity-complete-btn"]').click()
|
|
126
|
+
return this
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default ActivitiesPOM
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Companies Page Object Model for CRM Theme
|
|
3
|
+
*
|
|
4
|
+
* Entity-specific POM for Companies.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const companies = new CompaniesPOM()
|
|
8
|
+
* companies.list.validateTableVisible()
|
|
9
|
+
* companies.form.typeInField('name', 'Acme Inc')
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EntityList } from '../components/EntityList'
|
|
13
|
+
import { EntityForm } from '../components/EntityForm'
|
|
14
|
+
import { EntityDetail } from '../components/EntityDetail'
|
|
15
|
+
|
|
16
|
+
export class CompaniesPOM {
|
|
17
|
+
/** Generic list POM for companies */
|
|
18
|
+
readonly list: EntityList
|
|
19
|
+
|
|
20
|
+
/** Generic form POM for companies */
|
|
21
|
+
readonly form: EntityForm
|
|
22
|
+
|
|
23
|
+
/** Generic detail POM for companies */
|
|
24
|
+
readonly detail: EntityDetail
|
|
25
|
+
|
|
26
|
+
/** Company entity slug */
|
|
27
|
+
readonly slug = 'companies'
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
this.list = EntityList.for('companies')
|
|
31
|
+
this.form = EntityForm.for('companies')
|
|
32
|
+
this.detail = new EntityDetail('companies', 'company', ['contacts', 'opportunities', 'notes'])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================
|
|
36
|
+
// NAVIGATION METHODS
|
|
37
|
+
// ============================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Visit the companies list page
|
|
41
|
+
*/
|
|
42
|
+
visitList() {
|
|
43
|
+
cy.visit('/dashboard/companies')
|
|
44
|
+
this.list.waitForPageLoad()
|
|
45
|
+
return this
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Visit company detail page
|
|
50
|
+
*/
|
|
51
|
+
visitDetail(companyId: string) {
|
|
52
|
+
this.detail.visit(companyId)
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Visit create company page
|
|
58
|
+
*/
|
|
59
|
+
visitCreate() {
|
|
60
|
+
this.form.visitCreate()
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Visit edit company page
|
|
66
|
+
*/
|
|
67
|
+
visitEdit(companyId: string) {
|
|
68
|
+
this.form.visitEdit(companyId)
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================
|
|
73
|
+
// FORM HELPERS
|
|
74
|
+
// ============================================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Fill company form with common fields
|
|
78
|
+
*/
|
|
79
|
+
fillCompanyForm(data: {
|
|
80
|
+
name?: string
|
|
81
|
+
website?: string
|
|
82
|
+
industry?: string
|
|
83
|
+
size?: string
|
|
84
|
+
phone?: string
|
|
85
|
+
address?: string
|
|
86
|
+
}) {
|
|
87
|
+
if (data.name) {
|
|
88
|
+
this.form.typeInField('name', data.name)
|
|
89
|
+
}
|
|
90
|
+
if (data.website) {
|
|
91
|
+
this.form.typeInField('website', data.website)
|
|
92
|
+
}
|
|
93
|
+
if (data.industry) {
|
|
94
|
+
this.form.selectOption('industry', data.industry)
|
|
95
|
+
}
|
|
96
|
+
if (data.size) {
|
|
97
|
+
this.form.selectOption('size', data.size)
|
|
98
|
+
}
|
|
99
|
+
if (data.phone) {
|
|
100
|
+
this.form.typeInField('phone', data.phone)
|
|
101
|
+
}
|
|
102
|
+
if (data.address) {
|
|
103
|
+
this.form.typeInTextarea('address', data.address)
|
|
104
|
+
}
|
|
105
|
+
return this
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Submit the company form
|
|
110
|
+
*/
|
|
111
|
+
submitForm() {
|
|
112
|
+
this.form.submit()
|
|
113
|
+
return this
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export default CompaniesPOM
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contacts Page Object Model for CRM Theme
|
|
3
|
+
*
|
|
4
|
+
* Entity-specific POM for Contacts.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const contacts = new ContactsPOM()
|
|
8
|
+
* contacts.list.validateTableVisible()
|
|
9
|
+
* contacts.form.typeInField('firstName', 'John')
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EntityList } from '../components/EntityList'
|
|
13
|
+
import { EntityForm } from '../components/EntityForm'
|
|
14
|
+
import { EntityDetail } from '../components/EntityDetail'
|
|
15
|
+
|
|
16
|
+
export class ContactsPOM {
|
|
17
|
+
/** Generic list POM for contacts */
|
|
18
|
+
readonly list: EntityList
|
|
19
|
+
|
|
20
|
+
/** Generic form POM for contacts */
|
|
21
|
+
readonly form: EntityForm
|
|
22
|
+
|
|
23
|
+
/** Generic detail POM for contacts */
|
|
24
|
+
readonly detail: EntityDetail
|
|
25
|
+
|
|
26
|
+
/** Contact entity slug */
|
|
27
|
+
readonly slug = 'contacts'
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
this.list = EntityList.for('contacts')
|
|
31
|
+
this.form = EntityForm.for('contacts')
|
|
32
|
+
this.detail = new EntityDetail('contacts', 'contact', ['activities', 'notes', 'opportunities'])
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================
|
|
36
|
+
// NAVIGATION METHODS
|
|
37
|
+
// ============================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Visit the contacts list page
|
|
41
|
+
*/
|
|
42
|
+
visitList() {
|
|
43
|
+
cy.visit('/dashboard/contacts')
|
|
44
|
+
this.list.waitForPageLoad()
|
|
45
|
+
return this
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Visit contact detail page
|
|
50
|
+
*/
|
|
51
|
+
visitDetail(contactId: string) {
|
|
52
|
+
this.detail.visit(contactId)
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Visit create contact page
|
|
58
|
+
*/
|
|
59
|
+
visitCreate() {
|
|
60
|
+
this.form.visitCreate()
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Visit edit contact page
|
|
66
|
+
*/
|
|
67
|
+
visitEdit(contactId: string) {
|
|
68
|
+
this.form.visitEdit(contactId)
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================
|
|
73
|
+
// FORM HELPERS
|
|
74
|
+
// ============================================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Fill contact form with common fields
|
|
78
|
+
*/
|
|
79
|
+
fillContactForm(data: {
|
|
80
|
+
firstName?: string
|
|
81
|
+
lastName?: string
|
|
82
|
+
email?: string
|
|
83
|
+
phone?: string
|
|
84
|
+
companyId?: string
|
|
85
|
+
title?: string
|
|
86
|
+
}) {
|
|
87
|
+
if (data.firstName) {
|
|
88
|
+
this.form.typeInField('firstName', data.firstName)
|
|
89
|
+
}
|
|
90
|
+
if (data.lastName) {
|
|
91
|
+
this.form.typeInField('lastName', data.lastName)
|
|
92
|
+
}
|
|
93
|
+
if (data.email) {
|
|
94
|
+
this.form.typeInField('email', data.email)
|
|
95
|
+
}
|
|
96
|
+
if (data.phone) {
|
|
97
|
+
this.form.typeInField('phone', data.phone)
|
|
98
|
+
}
|
|
99
|
+
if (data.companyId) {
|
|
100
|
+
this.form.selectOption('companyId', data.companyId)
|
|
101
|
+
}
|
|
102
|
+
if (data.title) {
|
|
103
|
+
this.form.typeInField('title', data.title)
|
|
104
|
+
}
|
|
105
|
+
return this
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Submit the contact form
|
|
110
|
+
*/
|
|
111
|
+
submitForm() {
|
|
112
|
+
this.form.submit()
|
|
113
|
+
return this
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export default ContactsPOM
|