@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,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