@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.
Files changed (220) hide show
  1. package/package.json +1 -1
  2. package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
  3. package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
  4. package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
  5. package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
  6. package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
  7. package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
  8. package/tests/cypress/e2e/_docs/README.md +215 -0
  9. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
  10. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
  11. package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
  12. package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
  13. package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
  14. package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
  15. package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
  16. package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
  17. package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
  18. package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
  19. package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
  20. package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
  21. package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
  22. package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
  23. package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
  24. package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
  25. package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
  26. package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
  27. package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
  28. package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
  29. package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
  30. package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
  31. package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
  32. package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
  33. package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
  34. package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
  35. package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
  36. package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
  37. package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
  38. package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
  39. package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
  40. package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
  41. package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
  42. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  43. package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
  44. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  45. package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
  46. package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
  47. package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
  48. package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
  49. package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
  50. package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
  51. package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
  52. package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
  53. package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
  54. package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
  55. package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
  56. package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
  57. package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
  58. package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
  59. package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
  60. package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
  61. package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
  62. package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
  63. package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
  64. package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
  65. package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
  66. package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
  67. package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
  68. package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
  69. package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
  70. package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
  71. package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
  72. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
  73. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
  74. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  75. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  76. package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
  77. package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
  78. package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
  79. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
  80. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
  81. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
  82. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
  83. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
  84. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
  85. package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
  86. package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
  87. package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
  88. package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
  89. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
  90. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
  91. package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
  92. package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
  93. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
  94. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
  95. package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
  96. package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
  97. package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
  98. package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
  99. package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
  100. package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
  101. package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
  102. package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
  103. package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
  104. package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
  105. package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
  106. package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
  107. package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
  108. package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
  109. package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
  110. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  111. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  112. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  113. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  114. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  115. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  116. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  117. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  118. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  119. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  120. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  121. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  122. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  123. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  124. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  125. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  126. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  127. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  128. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  129. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  130. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  131. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  132. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  133. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  134. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  135. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  136. package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
  137. package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
  138. package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
  139. package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
  140. package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
  141. package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
  142. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
  143. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
  144. package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
  145. package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
  146. package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
  147. package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
  148. package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
  149. package/tests/cypress/fixtures/blocks.json +218 -0
  150. package/tests/cypress/fixtures/entities.json +78 -0
  151. package/tests/cypress/fixtures/page-builder.json +21 -0
  152. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  153. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  154. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  155. package/tests/cypress/src/components/EntityForm.ts +375 -0
  156. package/tests/cypress/src/components/EntityList.ts +389 -0
  157. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  158. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  159. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  160. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  161. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  162. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  163. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  164. package/tests/cypress/src/components/index.ts +21 -0
  165. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  166. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  167. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  168. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  169. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  170. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  171. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  172. package/tests/cypress/src/controllers/index.js +25 -0
  173. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  174. package/tests/cypress/src/core/BasePOM.ts +86 -0
  175. package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
  176. package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
  177. package/tests/cypress/src/core/index.ts +14 -0
  178. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  179. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  180. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  181. package/tests/cypress/src/entities/TasksPOM.ts +176 -0
  182. package/tests/cypress/src/entities/index.ts +14 -0
  183. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  184. package/tests/cypress/src/features/DashboardPOM.ts +245 -0
  185. package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
  186. package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
  187. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  188. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  189. package/tests/cypress/src/features/SettingsPOM.ts +362 -0
  190. package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
  191. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  192. package/tests/cypress/src/features/index.ts +28 -0
  193. package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
  194. package/tests/cypress/src/index.ts +101 -0
  195. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  196. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  197. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  198. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  199. package/tests/cypress/src/pages/index.js +5 -0
  200. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  201. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  202. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  203. package/tests/cypress/src/pages/public/index.js +6 -0
  204. package/tests/cypress/src/selectors.ts +46 -0
  205. package/tests/cypress/src/session-helpers.ts +500 -0
  206. package/tests/cypress/support/doc-commands.ts +260 -0
  207. package/tests/cypress.config.ts +150 -0
  208. package/tests/jest/components/post-header.test.tsx +377 -0
  209. package/tests/jest/config/role-config.test.ts +529 -0
  210. package/tests/jest/jest.config.ts +81 -0
  211. package/tests/jest/langchain/COVERAGE.md +372 -0
  212. package/tests/jest/langchain/guardrails.test.ts +465 -0
  213. package/tests/jest/langchain/streaming.test.ts +367 -0
  214. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  215. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  216. package/tests/jest/langchain/tracer.test.ts +823 -0
  217. package/tests/jest/user-roles/role-helpers.test.ts +432 -0
  218. package/tests/jest/validation/categories.test.ts +429 -0
  219. package/tests/jest/validation/posts.test.ts +546 -0
  220. package/tests/tsconfig.json +15 -0
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Tasks Entity POM
3
+ *
4
+ * Entity-specific Page Object Model for Tasks in Default theme.
5
+ * Extends generic EntityList/EntityForm with tasks-specific methods.
6
+ *
7
+ * Convention: tasks-{component}-{detail}
8
+ * Examples: tasks-form, tasks-field-title, tasks-row-{id}
9
+ */
10
+
11
+ import entitiesConfig from '../../fixtures/entities.json'
12
+
13
+ const tasksEntity = entitiesConfig.entities.tasks
14
+ const slug = tasksEntity.slug
15
+
16
+ export interface TaskFormData {
17
+ title: string
18
+ description?: string
19
+ status?: 'todo' | 'in-progress' | 'review' | 'done' | 'blocked'
20
+ priority?: 'low' | 'medium' | 'high' | 'urgent'
21
+ dueDate?: string
22
+ estimatedHours?: string
23
+ completed?: boolean
24
+ }
25
+
26
+ export class TasksPOM {
27
+ // ============================================
28
+ // STATIC CONFIG
29
+ // ============================================
30
+
31
+ static get entityConfig() {
32
+ return tasksEntity
33
+ }
34
+
35
+ static get slug() {
36
+ return slug
37
+ }
38
+
39
+ // ============================================
40
+ // SELECTORS
41
+ // ============================================
42
+
43
+ static get selectors() {
44
+ return {
45
+ // List page (EntityList uses slug-{element} convention)
46
+ page: `[data-cy="${slug}-list"]`, // Container div
47
+ table: `[data-cy="${slug}-table"]`, // Table element
48
+ createBtn: `[data-cy="${slug}-add"]`, // Add/Create button
49
+ searchInput: `[data-cy="${slug}-search"]`, // Search input
50
+ rowGeneric: `[data-cy^="${slug}-row-"]`, // Any row (prefix match)
51
+ row: (id: string) => `[data-cy="${slug}-row-${id}"]`,
52
+
53
+ // Form page
54
+ formPage: `[data-cy="${slug}-form-page"]`,
55
+ form: `[data-cy="${slug}-form"]`,
56
+ formSubmit: `[data-cy="${slug}-form-submit"]`,
57
+ formCancel: `[data-cy="${slug}-form-cancel"]`,
58
+
59
+ // Fields
60
+ field: (name: string) => `[data-cy="${slug}-field-${name}"]`,
61
+ fieldInput: (name: string) => `[data-cy="${slug}-field-${name}"] input`,
62
+ fieldTextarea: (name: string) => `[data-cy="${slug}-field-${name}"] textarea`,
63
+ fieldSelect: (name: string) => `[data-cy="${slug}-field-${name}"] [role="combobox"]`,
64
+ fieldOption: (name: string, value: string) => `[data-cy="${slug}-field-${name}-option-${value}"]`,
65
+
66
+ // Row menu actions (EntityTable patterns)
67
+ menuTrigger: (id: string) => `[data-cy="${slug}-menu-${id}"]`,
68
+ menuEdit: (id: string) => `[data-cy="${slug}-menu-edit-${id}"]`,
69
+ menuDelete: (id: string) => `[data-cy="${slug}-menu-delete-${id}"]`,
70
+ menuView: (id: string) => `[data-cy="${slug}-menu-view-${id}"]`,
71
+
72
+ // Dialogs
73
+ confirmDelete: `[data-cy="${slug}-confirm-delete"]`,
74
+ confirmDeleteBtn: `[data-cy="${slug}-confirm-delete-btn"]`,
75
+ cancelDeleteBtn: `[data-cy="${slug}-cancel-delete-btn"]`,
76
+ }
77
+ }
78
+
79
+ // ============================================
80
+ // NAVIGATION
81
+ // ============================================
82
+
83
+ static visitList() {
84
+ cy.visit(`/dashboard/${slug}`)
85
+ return this
86
+ }
87
+
88
+ static visitCreate() {
89
+ cy.visit(`/dashboard/${slug}/create`)
90
+ return this
91
+ }
92
+
93
+ static visitEdit(id: string) {
94
+ cy.visit(`/dashboard/${slug}/${id}/edit`)
95
+ return this
96
+ }
97
+
98
+ // ============================================
99
+ // WAIT METHODS
100
+ // ============================================
101
+
102
+ static waitForListLoad() {
103
+ cy.url().should('include', `/dashboard/${slug}`)
104
+ cy.get(this.selectors.page, { timeout: 15000 }).should('be.visible')
105
+ return this
106
+ }
107
+
108
+ static waitForFormLoad() {
109
+ cy.get(this.selectors.form, { timeout: 15000 }).should('be.visible')
110
+ return this
111
+ }
112
+
113
+ // ============================================
114
+ // LIST INTERACTIONS
115
+ // ============================================
116
+
117
+ static clickCreate() {
118
+ cy.get(this.selectors.createBtn).click()
119
+ return this
120
+ }
121
+
122
+ static search(term: string) {
123
+ cy.get(this.selectors.searchInput).clear().type(term)
124
+ return this
125
+ }
126
+
127
+ static clearSearch() {
128
+ cy.get(this.selectors.searchInput).clear()
129
+ return this
130
+ }
131
+
132
+ // ============================================
133
+ // FORM METHODS
134
+ // ============================================
135
+
136
+ static fillTextField(fieldName: string, value: string) {
137
+ cy.get(this.selectors.fieldInput(fieldName)).clear().type(value)
138
+ return this
139
+ }
140
+
141
+ static fillTextarea(fieldName: string, value: string) {
142
+ cy.get(this.selectors.fieldTextarea(fieldName)).clear().type(value)
143
+ return this
144
+ }
145
+
146
+ static selectOption(fieldName: string, optionValue: string) {
147
+ cy.get(this.selectors.fieldSelect(fieldName)).click()
148
+ cy.get(this.selectors.fieldOption(fieldName, optionValue)).click()
149
+ return this
150
+ }
151
+
152
+ static submitForm() {
153
+ cy.get(this.selectors.formSubmit).click()
154
+ return this
155
+ }
156
+
157
+ static cancelForm() {
158
+ cy.get(this.selectors.formCancel).click()
159
+ return this
160
+ }
161
+
162
+ /**
163
+ * Fill task form with provided data
164
+ */
165
+ static fillTaskForm(data: TaskFormData) {
166
+ if (data.title) {
167
+ this.fillTextField('title', data.title)
168
+ }
169
+ if (data.description) {
170
+ this.fillTextarea('description', data.description)
171
+ }
172
+ if (data.status) {
173
+ this.selectOption('status', data.status)
174
+ }
175
+ if (data.priority) {
176
+ this.selectOption('priority', data.priority)
177
+ }
178
+ if (data.dueDate) {
179
+ this.fillTextField('dueDate', data.dueDate)
180
+ }
181
+ if (data.estimatedHours) {
182
+ this.fillTextField('estimatedHours', data.estimatedHours)
183
+ }
184
+ return this
185
+ }
186
+
187
+ // ============================================
188
+ // ACTIONS
189
+ // ============================================
190
+
191
+ static openRowMenu(id: string) {
192
+ cy.get(this.selectors.menuTrigger(id)).click()
193
+ return this
194
+ }
195
+
196
+ static clickMenuEdit(id: string) {
197
+ cy.get(this.selectors.menuEdit(id)).click()
198
+ return this
199
+ }
200
+
201
+ static clickMenuDelete(id: string) {
202
+ cy.get(this.selectors.menuDelete(id)).click()
203
+ return this
204
+ }
205
+
206
+ static clickMenuView(id: string) {
207
+ cy.get(this.selectors.menuView(id)).click()
208
+ return this
209
+ }
210
+
211
+ static confirmDelete() {
212
+ cy.get(this.selectors.confirmDeleteBtn).click()
213
+ return this
214
+ }
215
+
216
+ static cancelDelete() {
217
+ cy.get(this.selectors.cancelDeleteBtn).click()
218
+ return this
219
+ }
220
+
221
+ /**
222
+ * Delete a task by finding it in the list and clicking delete
223
+ */
224
+ static deleteTaskByText(text: string) {
225
+ cy.contains(this.selectors.rowGeneric, text).within(() => {
226
+ cy.get('[data-cy*="delete"], button[aria-label*="delete" i]').click()
227
+ })
228
+ this.confirmDelete()
229
+ return this
230
+ }
231
+
232
+ /**
233
+ * Edit a task by finding it in the list and clicking edit
234
+ */
235
+ static editTaskByText(text: string) {
236
+ cy.contains(this.selectors.rowGeneric, text).within(() => {
237
+ cy.get('[data-cy*="edit"], button[aria-label*="edit" i]').click()
238
+ })
239
+ return this
240
+ }
241
+
242
+ // ============================================
243
+ // ASSERTIONS
244
+ // ============================================
245
+
246
+ static assertTaskInList(title: string) {
247
+ cy.contains(title).should('be.visible')
248
+ return this
249
+ }
250
+
251
+ static assertTaskNotInList(title: string) {
252
+ cy.contains(title).should('not.exist')
253
+ return this
254
+ }
255
+
256
+ static assertPageVisible() {
257
+ cy.get(this.selectors.page).should('be.visible')
258
+ return this
259
+ }
260
+
261
+ static assertFormVisible() {
262
+ cy.get(this.selectors.form).should('be.visible')
263
+ return this
264
+ }
265
+
266
+ static assertTableVisible() {
267
+ cy.get(this.selectors.table).should('be.visible')
268
+ return this
269
+ }
270
+ }
271
+
272
+ export default TasksPOM
@@ -0,0 +1,450 @@
1
+ /**
2
+ * TeamSwitcherPOM - Page Object Model for TeamSwitcherCompact component
3
+ *
4
+ * POM for the team switcher in multi-tenant mode.
5
+ * Used in sidebar footer and mobile MobileMoreSheet.
6
+ *
7
+ * @version 3.0 - Uses centralized selectors from cySelector()
8
+ */
9
+ import { BasePOM } from '../core/BasePOM'
10
+ import { cySelector } from '../selectors'
11
+
12
+ export class TeamSwitcherPOM extends BasePOM {
13
+ /**
14
+ * Selectors using centralized cySelector()
15
+ */
16
+ get selectors() {
17
+ return {
18
+ // TeamSwitcherCompact selectors
19
+ trigger: cySelector('teams.switcher.compact'),
20
+ dropdown: cySelector('teams.switcher.dropdown'),
21
+ // Prefix selector for all team options
22
+ teamOption: '[data-cy^="team-option-"]',
23
+ teamOptionBySlug: (slug: string) => cySelector('teams.switcher.option', { slug }),
24
+ manageTeamsLink: cySelector('teams.switcher.manageLink'),
25
+
26
+ // TeamSwitchModal selectors
27
+ switchModal: cySelector('teams.switchModal.container'),
28
+
29
+ // Sidebar selectors (toggle is in TopNavbar, not sidebar)
30
+ sidebar: cySelector('dashboard.sidebar.main'),
31
+ sidebarToggle: cySelector('dashboard.topnav.sidebarToggle'),
32
+
33
+ // Mobile selectors
34
+ mobileMoreButton: cySelector('dashboard.mobile.bottomNav.item', { id: 'more' }),
35
+ mobileMoreSheet: cySelector('dashboard.mobile.moreSheet.content'),
36
+ mobileTeamSwitcher: cySelector('dashboard.mobile.moreSheet.teamSwitcher'),
37
+
38
+ // Permission selectors
39
+ permissionDenied: cySelector('common.permissionDenied'),
40
+
41
+ // Internal elements (CSS classes, used for validation)
42
+ teamName: '.text-sm.font-medium',
43
+ teamRole: '.text-xs.text-muted-foreground.capitalize',
44
+ checkIcon: 'svg.lucide-check',
45
+ avatar: '[class*="avatar"]',
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Factory method - creates a new instance
51
+ */
52
+ static create(): TeamSwitcherPOM {
53
+ return new TeamSwitcherPOM()
54
+ }
55
+
56
+ // ============================================
57
+ // Sidebar Methods
58
+ // ============================================
59
+
60
+ /**
61
+ * Ensure sidebar is expanded (required for TeamSwitcher to be visible)
62
+ * The TeamSwitcherCompact only renders when sidebar is NOT collapsed
63
+ */
64
+ ensureSidebarExpanded(): this {
65
+ // Wait for dashboard to be fully loaded
66
+ cy.url().should('include', '/dashboard')
67
+
68
+ // First check if sidebar exists and wait for it to be visible
69
+ cy.get(this.selectors.sidebar, { timeout: 15000 }).should('be.visible')
70
+
71
+ // Check if collapsed and expand if needed
72
+ cy.get(this.selectors.sidebar).then($sidebar => {
73
+ const isCollapsed = $sidebar.attr('data-collapsed')
74
+ if (isCollapsed === 'true') {
75
+ // Click toggle to expand
76
+ cy.get(this.selectors.sidebarToggle, { timeout: 5000 })
77
+ .should('be.visible')
78
+ .click({ force: true })
79
+ // Wait for expansion animation
80
+ cy.get(this.selectors.sidebar, { timeout: 5000 })
81
+ .should('have.attr', 'data-collapsed', 'false')
82
+ // Wait for team switcher to render
83
+ cy.wait(300)
84
+ }
85
+ })
86
+
87
+ // Final assertion that sidebar is expanded
88
+ cy.get(this.selectors.sidebar).should('have.attr', 'data-collapsed', 'false')
89
+ return this
90
+ }
91
+
92
+ /**
93
+ * Validate sidebar is visible and expanded
94
+ */
95
+ validateSidebarExpanded(): this {
96
+ cy.get(this.selectors.sidebar)
97
+ .should('be.visible')
98
+ .and('have.attr', 'data-collapsed', 'false')
99
+ return this
100
+ }
101
+
102
+ /**
103
+ * Validate sidebar is collapsed
104
+ */
105
+ validateSidebarCollapsed(): this {
106
+ cy.get(this.selectors.sidebar)
107
+ .should('be.visible')
108
+ .and('have.attr', 'data-collapsed', 'true')
109
+ return this
110
+ }
111
+
112
+ /**
113
+ * Click sidebar toggle button to collapse/expand
114
+ */
115
+ toggleSidebar(): this {
116
+ cy.get(this.selectors.sidebarToggle).click()
117
+ return this
118
+ }
119
+
120
+ /**
121
+ * Collapse sidebar and wait for animation
122
+ */
123
+ collapseSidebar(): this {
124
+ cy.get(this.selectors.sidebar).then($sidebar => {
125
+ if ($sidebar.attr('data-collapsed') === 'false') {
126
+ this.toggleSidebar()
127
+ cy.get(this.selectors.sidebar).should('have.attr', 'data-collapsed', 'true')
128
+ }
129
+ })
130
+ return this
131
+ }
132
+
133
+ /**
134
+ * Expand sidebar and wait for animation
135
+ */
136
+ expandSidebar(): this {
137
+ cy.get(this.selectors.sidebar).then($sidebar => {
138
+ if ($sidebar.attr('data-collapsed') === 'true') {
139
+ this.toggleSidebar()
140
+ cy.get(this.selectors.sidebar).should('have.attr', 'data-collapsed', 'false')
141
+ }
142
+ })
143
+ return this
144
+ }
145
+
146
+ // ============================================
147
+ // Team Switcher Core Methods
148
+ // ============================================
149
+
150
+ /**
151
+ * Validate team switcher is visible
152
+ */
153
+ validateSwitcherVisible(): this {
154
+ this.ensureSidebarExpanded()
155
+ cy.get(this.selectors.trigger).should('be.visible')
156
+ return this
157
+ }
158
+
159
+ /**
160
+ * Validate team switcher is NOT visible
161
+ */
162
+ validateSwitcherNotVisible(): this {
163
+ cy.get(this.selectors.trigger).should('not.exist')
164
+ return this
165
+ }
166
+
167
+ /**
168
+ * Open the team switcher dropdown (idempotent)
169
+ */
170
+ open(): this {
171
+ this.ensureSidebarExpanded()
172
+ cy.get('body').then($body => {
173
+ if ($body.find(this.selectors.dropdown).length === 0) {
174
+ cy.get(this.selectors.trigger).click()
175
+ }
176
+ cy.get(this.selectors.dropdown).should('be.visible')
177
+ })
178
+ return this
179
+ }
180
+
181
+ /**
182
+ * Close the team switcher dropdown
183
+ */
184
+ close(): this {
185
+ cy.get('body').click(0, 0)
186
+ cy.get(this.selectors.dropdown).should('not.exist')
187
+ return this
188
+ }
189
+
190
+ /**
191
+ * Get the current team name displayed in the trigger button
192
+ */
193
+ getCurrentTeamName(): Cypress.Chainable<string> {
194
+ this.ensureSidebarExpanded()
195
+ return cy.get(this.selectors.trigger)
196
+ .find(this.selectors.teamName)
197
+ .invoke('text')
198
+ }
199
+
200
+ /**
201
+ * Validate current team name in trigger button
202
+ */
203
+ validateCurrentTeamName(teamName: string): this {
204
+ this.ensureSidebarExpanded()
205
+ cy.get(this.selectors.trigger)
206
+ .find(this.selectors.teamName)
207
+ .should('contain', teamName)
208
+ return this
209
+ }
210
+
211
+ // ============================================
212
+ // Team Selection Methods
213
+ // ============================================
214
+
215
+ /**
216
+ * Select a specific team by slug (opens dropdown, clicks team)
217
+ */
218
+ selectTeam(teamSlug: string): this {
219
+ this.open()
220
+ cy.get(this.selectors.teamOptionBySlug(teamSlug)).click()
221
+ return this
222
+ }
223
+
224
+ /**
225
+ * Select a team and wait for switch to complete
226
+ */
227
+ switchToTeam(teamSlug: string): this {
228
+ this.selectTeam(teamSlug)
229
+ this.waitForSwitchComplete()
230
+ return this
231
+ }
232
+
233
+ /**
234
+ * Wait for the switch modal to appear and complete
235
+ */
236
+ waitForSwitchComplete(): this {
237
+ cy.get(this.selectors.switchModal, { timeout: 5000 }).should('be.visible')
238
+ cy.get(this.selectors.switchModal, { timeout: 10000 }).should('not.exist')
239
+ cy.url().should('include', '/dashboard')
240
+ return this
241
+ }
242
+
243
+ /**
244
+ * Validate the switch modal is visible
245
+ */
246
+ validateSwitchModalVisible(): this {
247
+ cy.get(this.selectors.switchModal).should('be.visible')
248
+ return this
249
+ }
250
+
251
+ // ============================================
252
+ // Team Validation Methods
253
+ // ============================================
254
+
255
+ /**
256
+ * Get the count of teams in the dropdown
257
+ */
258
+ getTeamCount(): Cypress.Chainable<number> {
259
+ this.open()
260
+ return cy.get(this.selectors.teamOption).its('length')
261
+ }
262
+
263
+ /**
264
+ * Validate number of teams in dropdown
265
+ */
266
+ validateTeamCount(count: number): this {
267
+ this.open()
268
+ cy.get(this.selectors.teamOption).should('have.length', count)
269
+ return this
270
+ }
271
+
272
+ /**
273
+ * Validate team exists in the list
274
+ */
275
+ validateTeamInList(teamSlug: string): this {
276
+ this.open()
277
+ cy.get(this.selectors.teamOptionBySlug(teamSlug)).should('exist')
278
+ return this
279
+ }
280
+
281
+ /**
282
+ * Validate team is NOT in the list
283
+ */
284
+ validateTeamNotInList(teamSlug: string): this {
285
+ this.open()
286
+ cy.get(this.selectors.teamOptionBySlug(teamSlug)).should('not.exist')
287
+ return this
288
+ }
289
+
290
+ /**
291
+ * Validate a team option shows the checkmark (is active)
292
+ */
293
+ validateTeamHasCheckmark(teamSlug: string): this {
294
+ this.open()
295
+ cy.get(this.selectors.teamOptionBySlug(teamSlug))
296
+ .find(this.selectors.checkIcon)
297
+ .should('exist')
298
+ return this
299
+ }
300
+
301
+ /**
302
+ * Validate a team option does NOT show the checkmark
303
+ */
304
+ validateTeamNoCheckmark(teamSlug: string): this {
305
+ this.open()
306
+ cy.get(this.selectors.teamOptionBySlug(teamSlug))
307
+ .find(this.selectors.checkIcon)
308
+ .should('not.exist')
309
+ return this
310
+ }
311
+
312
+ /**
313
+ * Validate the role displayed for a team
314
+ */
315
+ validateRoleDisplayed(teamSlug: string, role: string): this {
316
+ this.open()
317
+ cy.get(this.selectors.teamOptionBySlug(teamSlug))
318
+ .find('.text-xs.text-muted-foreground')
319
+ .should('contain.text', role)
320
+ return this
321
+ }
322
+
323
+ /**
324
+ * Validate team has an avatar or initials displayed
325
+ */
326
+ validateTeamHasAvatar(teamSlug: string): this {
327
+ this.open()
328
+ cy.get(this.selectors.teamOptionBySlug(teamSlug))
329
+ .find(this.selectors.avatar)
330
+ .should('exist')
331
+ return this
332
+ }
333
+
334
+ /**
335
+ * Get all team names from the dropdown
336
+ */
337
+ getTeamNames(): Cypress.Chainable<string[]> {
338
+ this.open()
339
+ return cy.get(this.selectors.teamOption)
340
+ .find('.truncate')
341
+ .then($elements => {
342
+ return Cypress._.map($elements, el => el.innerText)
343
+ })
344
+ }
345
+
346
+ // ============================================
347
+ // Manage Teams Methods
348
+ // ============================================
349
+
350
+ /**
351
+ * Click Manage Teams link
352
+ */
353
+ clickManageTeams(): this {
354
+ this.open()
355
+ cy.get(this.selectors.manageTeamsLink).click()
356
+ return this
357
+ }
358
+
359
+ /**
360
+ * Validate Manage Teams link is visible
361
+ */
362
+ validateManageTeamsVisible(): this {
363
+ this.open()
364
+ cy.get(this.selectors.manageTeamsLink).should('be.visible')
365
+ return this
366
+ }
367
+
368
+ /**
369
+ * Navigate to Manage Teams and validate URL
370
+ */
371
+ goToManageTeams(): this {
372
+ this.clickManageTeams()
373
+ cy.url().should('include', '/dashboard/settings/teams')
374
+ return this
375
+ }
376
+
377
+ // ============================================
378
+ // Mobile Methods
379
+ // ============================================
380
+
381
+ /**
382
+ * Open the mobile more sheet
383
+ */
384
+ openMobileMoreSheet(): this {
385
+ cy.get(this.selectors.mobileMoreButton).click()
386
+ cy.get(this.selectors.mobileMoreSheet).should('be.visible')
387
+ return this
388
+ }
389
+
390
+ /**
391
+ * Validate mobile more sheet is visible
392
+ */
393
+ validateMobileMoreSheetVisible(): this {
394
+ cy.get(this.selectors.mobileMoreSheet).should('be.visible')
395
+ return this
396
+ }
397
+
398
+ /**
399
+ * Validate team switcher is visible in mobile more sheet
400
+ */
401
+ validateMobileTeamSwitcherVisible(): this {
402
+ cy.get(this.selectors.mobileMoreSheet).within(() => {
403
+ cy.get(this.selectors.trigger).should('be.visible')
404
+ })
405
+ return this
406
+ }
407
+
408
+ /**
409
+ * Open team dropdown from mobile more sheet
410
+ */
411
+ openMobileTeamDropdown(): this {
412
+ cy.get(this.selectors.mobileMoreSheet).within(() => {
413
+ cy.get(this.selectors.trigger).click()
414
+ })
415
+ return this
416
+ }
417
+
418
+ /**
419
+ * Switch team from mobile view
420
+ */
421
+ switchToTeamMobile(teamSlug: string): this {
422
+ this.openMobileMoreSheet()
423
+ this.openMobileTeamDropdown()
424
+ cy.get(this.selectors.teamOptionBySlug(teamSlug)).click()
425
+ this.waitForSwitchComplete()
426
+ return this
427
+ }
428
+
429
+ // ============================================
430
+ // Permission Methods
431
+ // ============================================
432
+
433
+ /**
434
+ * Validate permission denied page is visible
435
+ */
436
+ validatePermissionDenied(): this {
437
+ cy.get(this.selectors.permissionDenied).should('be.visible')
438
+ return this
439
+ }
440
+
441
+ /**
442
+ * Validate permission denied page is NOT visible
443
+ */
444
+ validateNoPermissionDenied(): this {
445
+ cy.get(this.selectors.permissionDenied).should('not.exist')
446
+ return this
447
+ }
448
+ }
449
+
450
+ export default TeamSwitcherPOM