@nextsparkjs/theme-default 0.1.0-beta.20 → 0.1.0-beta.21
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 +1 -1
- package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
- package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
- package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
- package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
- package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
- package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
- package/tests/cypress/e2e/_docs/README.md +215 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
- package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
- package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
- package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
- package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
- package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
- package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
- package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
- package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
- package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
- package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
- package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
- package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
- package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
- package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
- package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
- package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
- package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
- package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
- package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
- package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
- package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
- package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
- package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
- package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
- package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
- package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
- package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
- package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
- package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
- package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
- package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
- package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
- package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
- package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
- package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
- package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
- package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
- package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
- package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
- package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
- package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
- package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
- package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
- package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
- package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
- package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
- package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
- package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
- package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
- package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
- package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
- package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
- package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
- package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
- package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
- package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
- package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
- package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
- package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
- package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
- package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
- package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
- package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
- package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
- package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
- package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
- package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
- package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
- package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
- package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
- package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
- package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
- package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
- package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
- package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
- package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
- package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
- package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
- package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
- package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
- package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
- package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
- package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
- package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
- package/tests/cypress/fixtures/blocks.json +218 -0
- package/tests/cypress/fixtures/entities.json +78 -0
- package/tests/cypress/fixtures/page-builder.json +21 -0
- package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
- package/tests/cypress/src/components/CustomersPOM.ts +439 -0
- package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
- package/tests/cypress/src/components/EntityForm.ts +375 -0
- package/tests/cypress/src/components/EntityList.ts +389 -0
- package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
- package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
- package/tests/cypress/src/components/PostsListPOM.ts +223 -0
- package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
- package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
- package/tests/cypress/src/components/TasksPOM.ts +272 -0
- package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
- package/tests/cypress/src/components/index.ts +21 -0
- package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
- package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
- package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
- package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
- package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
- package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
- package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
- package/tests/cypress/src/controllers/index.js +25 -0
- package/tests/cypress/src/core/AuthPOM.ts +450 -0
- package/tests/cypress/src/core/BasePOM.ts +86 -0
- package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
- package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
- package/tests/cypress/src/core/index.ts +14 -0
- package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
- package/tests/cypress/src/entities/PagesPOM.ts +137 -0
- package/tests/cypress/src/entities/PostsPOM.ts +137 -0
- package/tests/cypress/src/entities/TasksPOM.ts +176 -0
- package/tests/cypress/src/entities/index.ts +14 -0
- package/tests/cypress/src/features/BillingPOM.ts +385 -0
- package/tests/cypress/src/features/DashboardPOM.ts +245 -0
- package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
- package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
- package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
- package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
- package/tests/cypress/src/features/SettingsPOM.ts +362 -0
- package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
- package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
- package/tests/cypress/src/features/index.ts +28 -0
- package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
- package/tests/cypress/src/index.ts +101 -0
- package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
- package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
- package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
- package/tests/cypress/src/pages/dashboard/index.js +6 -0
- package/tests/cypress/src/pages/index.js +5 -0
- package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
- package/tests/cypress/src/pages/public/LandingPage.js +69 -0
- package/tests/cypress/src/pages/public/PricingPage.js +33 -0
- package/tests/cypress/src/pages/public/index.js +6 -0
- package/tests/cypress/src/selectors.ts +46 -0
- package/tests/cypress/src/session-helpers.ts +500 -0
- package/tests/cypress/support/doc-commands.ts +260 -0
- package/tests/cypress.config.ts +150 -0
- package/tests/jest/components/post-header.test.tsx +377 -0
- package/tests/jest/config/role-config.test.ts +529 -0
- package/tests/jest/jest.config.ts +81 -0
- package/tests/jest/langchain/COVERAGE.md +372 -0
- package/tests/jest/langchain/guardrails.test.ts +465 -0
- package/tests/jest/langchain/streaming.test.ts +367 -0
- package/tests/jest/langchain/token-tracker.test.ts +455 -0
- package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
- package/tests/jest/langchain/tracer.test.ts +823 -0
- package/tests/jest/user-roles/role-helpers.test.ts +432 -0
- package/tests/jest/validation/categories.test.ts +429 -0
- package/tests/jest/validation/posts.test.ts +546 -0
- package/tests/tsconfig.json +15 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customers Entity POM
|
|
3
|
+
*
|
|
4
|
+
* Entity-specific Page Object Model for Customers in Default theme.
|
|
5
|
+
* Extends generic EntityList/EntityForm with customer-specific methods.
|
|
6
|
+
* Includes API-aware methods for deterministic testing with cy.intercept().
|
|
7
|
+
*
|
|
8
|
+
* Convention: customers-{component}-{detail}
|
|
9
|
+
* Examples: customers-form, customers-field-name, customers-row-{id}
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import entitiesConfig from '../../fixtures/entities.json'
|
|
13
|
+
import { ApiInterceptor } from '../helpers/ApiInterceptor'
|
|
14
|
+
|
|
15
|
+
const customersEntity = entitiesConfig.entities.customers
|
|
16
|
+
const slug = customersEntity.slug
|
|
17
|
+
|
|
18
|
+
export interface CustomerFormData {
|
|
19
|
+
name: string
|
|
20
|
+
account: string
|
|
21
|
+
office: string
|
|
22
|
+
phone?: string
|
|
23
|
+
salesRep?: string
|
|
24
|
+
visitDays?: string[]
|
|
25
|
+
contactDays?: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class CustomersPOM {
|
|
29
|
+
// ============================================
|
|
30
|
+
// STATIC CONFIG
|
|
31
|
+
// ============================================
|
|
32
|
+
|
|
33
|
+
static get entityConfig() {
|
|
34
|
+
return customersEntity
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static get slug() {
|
|
38
|
+
return slug
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================
|
|
42
|
+
// API INTERCEPTOR (for deterministic waits)
|
|
43
|
+
// ============================================
|
|
44
|
+
|
|
45
|
+
private static _api: ApiInterceptor | null = null
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get the API interceptor instance for customers
|
|
49
|
+
* Lazy-initialized on first access
|
|
50
|
+
*/
|
|
51
|
+
static get api(): ApiInterceptor {
|
|
52
|
+
if (!this._api) {
|
|
53
|
+
this._api = new ApiInterceptor(slug)
|
|
54
|
+
}
|
|
55
|
+
return this._api
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Setup API intercepts for CRUD operations
|
|
60
|
+
* Call this in beforeEach BEFORE navigation
|
|
61
|
+
*/
|
|
62
|
+
static setupApiIntercepts(): typeof CustomersPOM {
|
|
63
|
+
this.api.setupCrudIntercepts()
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ============================================
|
|
68
|
+
// SELECTORS
|
|
69
|
+
// ============================================
|
|
70
|
+
|
|
71
|
+
static get selectors() {
|
|
72
|
+
return {
|
|
73
|
+
// List page (matches EntityList.tsx createCyId patterns)
|
|
74
|
+
page: `[data-cy="${slug}-list"]`,
|
|
75
|
+
table: `[data-cy="${slug}-table"]`,
|
|
76
|
+
createBtn: `[data-cy="${slug}-add"]`,
|
|
77
|
+
searchInput: `[data-cy="${slug}-search"]`,
|
|
78
|
+
rowGeneric: `[data-cy^="${slug}-row-"]`,
|
|
79
|
+
row: (id: string) => `[data-cy="${slug}-row-${id}"]`,
|
|
80
|
+
|
|
81
|
+
// Form page (EntityForm patterns)
|
|
82
|
+
formPage: `[data-cy="${slug}-form-page"]`,
|
|
83
|
+
form: `[data-cy="${slug}-form"]`,
|
|
84
|
+
formSubmit: `[data-cy="${slug}-form-submit"]`,
|
|
85
|
+
formCancel: `[data-cy="${slug}-form-cancel"]`,
|
|
86
|
+
|
|
87
|
+
// Fields (EntityForm field patterns)
|
|
88
|
+
field: (name: string) => `[data-cy="${slug}-field-${name}"]`,
|
|
89
|
+
fieldInput: (name: string) => `[data-cy="${slug}-field-${name}"] input`,
|
|
90
|
+
fieldTextarea: (name: string) => `[data-cy="${slug}-field-${name}"] textarea`,
|
|
91
|
+
fieldSelect: (name: string) => `[data-cy="${slug}-field-${name}"] [role="combobox"]`,
|
|
92
|
+
fieldOption: (name: string, value: string) => `[data-cy="${slug}-field-${name}-option-${value}"]`,
|
|
93
|
+
|
|
94
|
+
// Detail page actions (EntityDetail patterns)
|
|
95
|
+
detailEdit: `[data-cy="${slug}-edit"]`,
|
|
96
|
+
detailDelete: `[data-cy="${slug}-delete"]`,
|
|
97
|
+
|
|
98
|
+
// Row menu actions (EntityTable patterns)
|
|
99
|
+
menuTrigger: (id: string) => `[data-cy="${slug}-menu-${id}"]`,
|
|
100
|
+
menuEdit: (id: string) => `[data-cy="${slug}-menu-edit-${id}"]`,
|
|
101
|
+
menuDelete: (id: string) => `[data-cy="${slug}-menu-delete-${id}"]`,
|
|
102
|
+
menuView: (id: string) => `[data-cy="${slug}-menu-view-${id}"]`,
|
|
103
|
+
|
|
104
|
+
// Dialogs (EntityDetailWrapper patterns)
|
|
105
|
+
confirmDelete: `[data-cy="confirm-delete"]`,
|
|
106
|
+
confirmDeleteBtn: `[data-cy="confirm-delete"]`,
|
|
107
|
+
cancelDeleteBtn: `[data-cy="cancel-delete"]`,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ============================================
|
|
112
|
+
// NAVIGATION
|
|
113
|
+
// ============================================
|
|
114
|
+
|
|
115
|
+
static visitList() {
|
|
116
|
+
cy.visit(`/dashboard/${slug}`)
|
|
117
|
+
return this
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static visitCreate() {
|
|
121
|
+
cy.visit(`/dashboard/${slug}/create`)
|
|
122
|
+
return this
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static visitEdit(id: string) {
|
|
126
|
+
cy.visit(`/dashboard/${slug}/${id}/edit`)
|
|
127
|
+
return this
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static visitDetail(id: string) {
|
|
131
|
+
cy.visit(`/dashboard/${slug}/${id}`)
|
|
132
|
+
return this
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================
|
|
136
|
+
// API-AWARE NAVIGATION
|
|
137
|
+
// ============================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Visit list page with API intercepts and wait for data load
|
|
141
|
+
* Replaces: visitList() + cy.wait(2000)
|
|
142
|
+
*/
|
|
143
|
+
static visitListWithApiWait(): typeof CustomersPOM {
|
|
144
|
+
this.setupApiIntercepts()
|
|
145
|
+
this.visitList()
|
|
146
|
+
this.api.waitForList()
|
|
147
|
+
return this
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Visit edit page with API wait for data load
|
|
152
|
+
*/
|
|
153
|
+
static visitEditWithApiWait(id: string): typeof CustomersPOM {
|
|
154
|
+
this.setupApiIntercepts()
|
|
155
|
+
this.visitEdit(id)
|
|
156
|
+
this.waitForFormLoad()
|
|
157
|
+
return this
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Visit detail page with API wait for data load
|
|
162
|
+
*/
|
|
163
|
+
static visitDetailWithApiWait(id: string): typeof CustomersPOM {
|
|
164
|
+
this.setupApiIntercepts()
|
|
165
|
+
this.visitDetail(id)
|
|
166
|
+
this.waitForDetailLoad()
|
|
167
|
+
return this
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ============================================
|
|
171
|
+
// API-AWARE CRUD WORKFLOWS
|
|
172
|
+
// ============================================
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Create customer with deterministic API waits
|
|
176
|
+
* Replaces: fillForm() + submit() + cy.wait(2000)
|
|
177
|
+
*/
|
|
178
|
+
static createWithApiWait(data: CustomerFormData): typeof CustomersPOM {
|
|
179
|
+
this.clickCreate()
|
|
180
|
+
this.waitForFormLoad()
|
|
181
|
+
this.fillCustomerForm(data)
|
|
182
|
+
this.submitForm()
|
|
183
|
+
this.api.waitForCreate()
|
|
184
|
+
this.api.waitForRefresh()
|
|
185
|
+
return this
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Update customer with deterministic API waits
|
|
190
|
+
* Note: Does NOT include waitForRefresh() as behavior varies by UI
|
|
191
|
+
* (may stay on form or redirect to list). Add .api.waitForRefresh() if needed.
|
|
192
|
+
*/
|
|
193
|
+
static updateWithApiWait(data: Partial<CustomerFormData>): typeof CustomersPOM {
|
|
194
|
+
if (data.name) this.fillTextField('name', data.name)
|
|
195
|
+
if (data.account) this.fillTextField('account', data.account)
|
|
196
|
+
if (data.office) this.fillTextField('office', data.office)
|
|
197
|
+
if (data.phone) this.fillTextField('phone', data.phone)
|
|
198
|
+
if (data.salesRep) this.fillTextField('salesRep', data.salesRep)
|
|
199
|
+
this.submitForm()
|
|
200
|
+
this.api.waitForUpdate()
|
|
201
|
+
return this
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Delete customer with deterministic API waits
|
|
206
|
+
* Flow: Navigate to detail page -> Click delete -> Confirm -> Wait for delete
|
|
207
|
+
*/
|
|
208
|
+
static deleteWithApiWait(id: string): typeof CustomersPOM {
|
|
209
|
+
this.visitDetailWithApiWait(id)
|
|
210
|
+
this.clickDetailDelete()
|
|
211
|
+
this.confirmDelete()
|
|
212
|
+
this.api.waitForDelete()
|
|
213
|
+
return this
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Delete customer from list by name (with API waits)
|
|
218
|
+
* Finds customer, navigates to detail, deletes
|
|
219
|
+
*/
|
|
220
|
+
static deleteByNameWithApiWait(name: string): typeof CustomersPOM {
|
|
221
|
+
this.clickCustomerInList(name)
|
|
222
|
+
this.waitForDetailLoad()
|
|
223
|
+
this.clickDetailDelete()
|
|
224
|
+
this.confirmDelete()
|
|
225
|
+
this.api.waitForDelete()
|
|
226
|
+
return this
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ============================================
|
|
230
|
+
// WAIT METHODS
|
|
231
|
+
// ============================================
|
|
232
|
+
|
|
233
|
+
static waitForListLoad() {
|
|
234
|
+
cy.url().should('include', `/dashboard/${slug}`)
|
|
235
|
+
cy.get(this.selectors.page, { timeout: 15000 }).should('be.visible')
|
|
236
|
+
return this
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
static waitForFormLoad() {
|
|
240
|
+
cy.get(this.selectors.form, { timeout: 15000 }).should('be.visible')
|
|
241
|
+
return this
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
static waitForDetailLoad() {
|
|
245
|
+
cy.url().should('match', new RegExp(`/dashboard/${slug}/[a-z0-9-]+$`))
|
|
246
|
+
cy.get(this.selectors.detailEdit, { timeout: 15000 }).should('be.visible')
|
|
247
|
+
return this
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ============================================
|
|
251
|
+
// LIST INTERACTIONS
|
|
252
|
+
// ============================================
|
|
253
|
+
|
|
254
|
+
static clickCreate() {
|
|
255
|
+
cy.get(this.selectors.createBtn).click()
|
|
256
|
+
return this
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static search(term: string) {
|
|
260
|
+
cy.get(this.selectors.searchInput).clear().type(term)
|
|
261
|
+
return this
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static clearSearch() {
|
|
265
|
+
cy.get(this.selectors.searchInput).clear()
|
|
266
|
+
return this
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ============================================
|
|
270
|
+
// FORM METHODS
|
|
271
|
+
// ============================================
|
|
272
|
+
|
|
273
|
+
static fillTextField(fieldName: string, value: string) {
|
|
274
|
+
cy.get(this.selectors.fieldInput(fieldName)).clear().type(value)
|
|
275
|
+
return this
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
static fillTextarea(fieldName: string, value: string) {
|
|
279
|
+
cy.get(this.selectors.fieldTextarea(fieldName)).clear().type(value)
|
|
280
|
+
return this
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
static selectOption(fieldName: string, optionValue: string) {
|
|
284
|
+
cy.get(this.selectors.fieldSelect(fieldName)).click()
|
|
285
|
+
cy.get(this.selectors.fieldOption(fieldName, optionValue)).click()
|
|
286
|
+
return this
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
static submitForm() {
|
|
290
|
+
cy.get(this.selectors.formSubmit).click()
|
|
291
|
+
return this
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
static cancelForm() {
|
|
295
|
+
cy.get(this.selectors.formCancel).click()
|
|
296
|
+
return this
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ============================================
|
|
300
|
+
// DETAIL PAGE INTERACTIONS
|
|
301
|
+
// ============================================
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Click edit button on detail page
|
|
305
|
+
* (EntityDetail pattern - edit/delete are on detail page, not list)
|
|
306
|
+
* Uses .first() because there may be multiple edit buttons (header + quick actions)
|
|
307
|
+
*/
|
|
308
|
+
static clickDetailEdit() {
|
|
309
|
+
cy.get(this.selectors.detailEdit).first().click()
|
|
310
|
+
return this
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Click delete button on detail page
|
|
315
|
+
* Uses .first() because there may be multiple delete buttons
|
|
316
|
+
*/
|
|
317
|
+
static clickDetailDelete() {
|
|
318
|
+
cy.get(this.selectors.detailDelete).first().click()
|
|
319
|
+
return this
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Click customer name in list to navigate to detail page
|
|
324
|
+
*/
|
|
325
|
+
static clickCustomerInList(name: string) {
|
|
326
|
+
cy.contains(this.selectors.rowGeneric, name).within(() => {
|
|
327
|
+
cy.get('a').first().click()
|
|
328
|
+
})
|
|
329
|
+
return this
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Fill customer form with provided data
|
|
334
|
+
*/
|
|
335
|
+
static fillCustomerForm(data: CustomerFormData) {
|
|
336
|
+
if (data.name) {
|
|
337
|
+
this.fillTextField('name', data.name)
|
|
338
|
+
}
|
|
339
|
+
if (data.account) {
|
|
340
|
+
this.fillTextField('account', data.account)
|
|
341
|
+
}
|
|
342
|
+
if (data.office) {
|
|
343
|
+
this.fillTextField('office', data.office)
|
|
344
|
+
}
|
|
345
|
+
if (data.phone) {
|
|
346
|
+
this.fillTextField('phone', data.phone)
|
|
347
|
+
}
|
|
348
|
+
if (data.salesRep) {
|
|
349
|
+
this.fillTextField('salesRep', data.salesRep)
|
|
350
|
+
}
|
|
351
|
+
return this
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ============================================
|
|
355
|
+
// ACTIONS
|
|
356
|
+
// ============================================
|
|
357
|
+
|
|
358
|
+
static openRowMenu(id: string) {
|
|
359
|
+
cy.get(this.selectors.menuTrigger(id)).click()
|
|
360
|
+
return this
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
static clickMenuEdit(id: string) {
|
|
364
|
+
cy.get(this.selectors.menuEdit(id)).click()
|
|
365
|
+
return this
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
static clickMenuDelete(id: string) {
|
|
369
|
+
cy.get(this.selectors.menuDelete(id)).click()
|
|
370
|
+
return this
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
static clickMenuView(id: string) {
|
|
374
|
+
cy.get(this.selectors.menuView(id)).click()
|
|
375
|
+
return this
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
static confirmDelete() {
|
|
379
|
+
cy.get(this.selectors.confirmDeleteBtn).click()
|
|
380
|
+
return this
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
static cancelDelete() {
|
|
384
|
+
cy.get(this.selectors.cancelDeleteBtn).click()
|
|
385
|
+
return this
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Delete a customer by finding it in the list and clicking delete
|
|
390
|
+
*/
|
|
391
|
+
static deleteCustomerByText(text: string) {
|
|
392
|
+
cy.contains(this.selectors.rowGeneric, text).within(() => {
|
|
393
|
+
cy.get('[data-cy*="delete"], button[aria-label*="delete" i]').click()
|
|
394
|
+
})
|
|
395
|
+
this.confirmDelete()
|
|
396
|
+
return this
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Edit a customer by finding it in the list and clicking edit
|
|
401
|
+
*/
|
|
402
|
+
static editCustomerByText(text: string) {
|
|
403
|
+
cy.contains(this.selectors.rowGeneric, text).within(() => {
|
|
404
|
+
cy.get('[data-cy*="edit"], button[aria-label*="edit" i]').click()
|
|
405
|
+
})
|
|
406
|
+
return this
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ============================================
|
|
410
|
+
// ASSERTIONS
|
|
411
|
+
// ============================================
|
|
412
|
+
|
|
413
|
+
static assertCustomerInList(name: string) {
|
|
414
|
+
cy.contains(name).should('be.visible')
|
|
415
|
+
return this
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
static assertCustomerNotInList(name: string) {
|
|
419
|
+
cy.contains(name).should('not.exist')
|
|
420
|
+
return this
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
static assertPageVisible() {
|
|
424
|
+
cy.get(this.selectors.page).should('be.visible')
|
|
425
|
+
return this
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
static assertFormVisible() {
|
|
429
|
+
cy.get(this.selectors.form).should('be.visible')
|
|
430
|
+
return this
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
static assertTableVisible() {
|
|
434
|
+
cy.get(this.selectors.table).should('be.visible')
|
|
435
|
+
return this
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export default CustomersPOM
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DevKeyringPOM - Page Object Model for Development Keyring
|
|
3
|
+
*
|
|
4
|
+
* POM for the development keyring (test credential selector).
|
|
5
|
+
* This component allows quick switching between test users in development mode.
|
|
6
|
+
*
|
|
7
|
+
* NOTE: DevKeyring only FILLS the login form with credentials.
|
|
8
|
+
* You must still submit the form to complete login.
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: Always use email-based methods (selectUserByEmail, quickLoginByEmail)
|
|
11
|
+
* to make tests resilient to user order changes in the DevKeyring config.
|
|
12
|
+
*
|
|
13
|
+
* @version 3.0 - Uses centralized selectors from cySelector()
|
|
14
|
+
*/
|
|
15
|
+
import { BasePOM } from '../core/BasePOM'
|
|
16
|
+
import { cySelector } from '../selectors'
|
|
17
|
+
|
|
18
|
+
export class DevKeyringPOM extends BasePOM {
|
|
19
|
+
/**
|
|
20
|
+
* Selectors using centralized cySelector()
|
|
21
|
+
*/
|
|
22
|
+
get selectors() {
|
|
23
|
+
return {
|
|
24
|
+
container: cySelector('auth.devKeyring.container'),
|
|
25
|
+
trigger: cySelector('auth.devKeyring.trigger'),
|
|
26
|
+
content: cySelector('auth.devKeyring.content'),
|
|
27
|
+
// Prefix selector for all user items (cySelector doesn't support prefix matching)
|
|
28
|
+
userItem: '[data-cy^="devkeyring-user-"]',
|
|
29
|
+
userByIndex: (index: number) => cySelector('auth.devKeyring.user', { index }),
|
|
30
|
+
// Login form selectors (used after filling credentials)
|
|
31
|
+
loginSubmit: cySelector('auth.login.submit'),
|
|
32
|
+
loginEmail: '#email',
|
|
33
|
+
loginPassword: '#password',
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Factory method - creates a new instance
|
|
39
|
+
*/
|
|
40
|
+
static create(): DevKeyringPOM {
|
|
41
|
+
return new DevKeyringPOM()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================
|
|
45
|
+
// Validation Methods
|
|
46
|
+
// ============================================
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate keyring container is visible
|
|
50
|
+
*/
|
|
51
|
+
validateVisible(): this {
|
|
52
|
+
cy.get(this.selectors.container).should('be.visible')
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Validate keyring is not visible (production mode)
|
|
58
|
+
*/
|
|
59
|
+
validateNotVisible(): this {
|
|
60
|
+
cy.get(this.selectors.container).should('not.exist')
|
|
61
|
+
return this
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================
|
|
65
|
+
// Dropdown Methods
|
|
66
|
+
// ============================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Open the keyring dropdown
|
|
70
|
+
*/
|
|
71
|
+
open(): this {
|
|
72
|
+
cy.get(this.selectors.trigger).click()
|
|
73
|
+
cy.get(this.selectors.content).should('be.visible')
|
|
74
|
+
return this
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Close the keyring dropdown
|
|
79
|
+
*/
|
|
80
|
+
close(): this {
|
|
81
|
+
cy.get('body').click(0, 0)
|
|
82
|
+
cy.get(this.selectors.content).should('not.be.visible')
|
|
83
|
+
return this
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ============================================
|
|
87
|
+
// User Selection Methods
|
|
88
|
+
// ============================================
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Select a user by email (fills the form, does NOT submit)
|
|
92
|
+
*/
|
|
93
|
+
selectUserByEmail(email: string): this {
|
|
94
|
+
this.open()
|
|
95
|
+
cy.get(this.selectors.userItem).contains(email).click()
|
|
96
|
+
// Wait for form to be filled
|
|
97
|
+
cy.get(this.selectors.loginEmail).should('have.value', email)
|
|
98
|
+
return this
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Submit the login form after credentials are filled
|
|
103
|
+
*/
|
|
104
|
+
submitLogin(): this {
|
|
105
|
+
cy.get(this.selectors.loginSubmit).click()
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Quick login with a specific user by email (fills form AND submits)
|
|
111
|
+
* This is the preferred method for login as it's resilient to user order changes.
|
|
112
|
+
*/
|
|
113
|
+
quickLoginByEmail(email: string): this {
|
|
114
|
+
// 1. Select user by email (opens dropdown + fills form)
|
|
115
|
+
this.selectUserByEmail(email)
|
|
116
|
+
|
|
117
|
+
// 2. Submit the login form
|
|
118
|
+
this.submitLogin()
|
|
119
|
+
|
|
120
|
+
// 3. Wait for login to complete
|
|
121
|
+
cy.url().should('include', '/dashboard', { timeout: 10000 })
|
|
122
|
+
|
|
123
|
+
return this
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ============================================
|
|
127
|
+
// User Validation Methods
|
|
128
|
+
// ============================================
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate the number of available users
|
|
132
|
+
*/
|
|
133
|
+
validateUserCount(count: number): this {
|
|
134
|
+
this.open()
|
|
135
|
+
cy.get(this.selectors.userItem).should('have.length', count)
|
|
136
|
+
return this
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Validate a user exists in the keyring
|
|
141
|
+
*/
|
|
142
|
+
validateUserExists(email: string): this {
|
|
143
|
+
this.open()
|
|
144
|
+
cy.get(this.selectors.userItem).contains(email).should('exist')
|
|
145
|
+
return this
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get all user emails from the keyring
|
|
150
|
+
*/
|
|
151
|
+
getUserEmails(): Cypress.Chainable<string[]> {
|
|
152
|
+
this.open()
|
|
153
|
+
return cy.get(this.selectors.userItem)
|
|
154
|
+
.then($elements => {
|
|
155
|
+
return Cypress._.map($elements, el => el.innerText.trim())
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export default DevKeyringPOM
|