@nextsparkjs/theme-default 0.1.0-beta.38 → 0.1.0-beta.40

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 (25) hide show
  1. package/components/ai-chat/ChatPanel.tsx +1 -1
  2. package/components/ai-chat/Message.tsx +1 -1
  3. package/components/ai-chat/MessageInput.tsx +1 -1
  4. package/components/ai-chat/MessageList.tsx +1 -1
  5. package/components/ai-chat/TypingIndicator.tsx +1 -1
  6. package/lib/selectors.ts +2 -3
  7. package/package.json +3 -2
  8. package/tests/cypress/e2e/api/_core/billing/BillingAPIController.js +1 -1
  9. package/tests/cypress/e2e/api/_core/settings/api-keys.crud.cy.ts +1 -1
  10. package/tests/cypress/e2e/api/_core/users/users-crud.cy.ts +1 -1
  11. package/tests/cypress/e2e/api/_core/users/users-metas.cy.ts +1 -1
  12. package/tests/cypress/e2e/api/entities/customers/customers-crud.cy.ts +1 -1
  13. package/tests/cypress/e2e/api/entities/customers/customers-metas.cy.ts +1 -1
  14. package/tests/cypress/e2e/api/entities/tasks/tasks-crud.cy.ts +1 -1
  15. package/tests/cypress/e2e/api/entities/tasks/tasks-metas.cy.ts +1 -1
  16. package/tests/cypress/e2e/uat/_core/teams/team-switcher.md +2 -2
  17. package/tests/cypress/src/core/BasePOM.ts +23 -76
  18. package/tests/cypress/src/core/DashboardEntityPOM.ts +17 -668
  19. package/tests/cypress/src/entities/TasksPOM.ts +70 -0
  20. package/tests/cypress/src/helpers/ApiInterceptor.ts +14 -171
  21. package/tests/cypress.config.ts +12 -17
  22. package/tests/cypress/e2e/_utils/docs/tutorials/sector7-superadmin-teams.narration.json +0 -155
  23. package/tests/cypress/e2e/_utils/docs/tutorials/sector7-superadmin.cy.ts +0 -390
  24. package/tests/cypress/e2e/_utils/docs/tutorials/teams-system.doc.cy.ts +0 -349
  25. package/tests/cypress/e2e/_utils/docs/tutorials/teams-system.narration.json +0 -165
@@ -154,6 +154,42 @@ export class TasksPOM extends DashboardEntityPOM {
154
154
  return this
155
155
  }
156
156
 
157
+ // ============================================
158
+ // FILTER METHODS
159
+ // ============================================
160
+
161
+ /**
162
+ * Filter tasks by status
163
+ */
164
+ filterByStatus(status: string) {
165
+ this.selectFilter('status', status)
166
+ return this
167
+ }
168
+
169
+ /**
170
+ * Filter tasks by priority
171
+ */
172
+ filterByPriority(priority: string) {
173
+ this.selectFilter('priority', priority)
174
+ return this
175
+ }
176
+
177
+ /**
178
+ * Clear status filter
179
+ */
180
+ clearStatusFilter() {
181
+ this.clearFilter('status')
182
+ return this
183
+ }
184
+
185
+ /**
186
+ * Clear priority filter
187
+ */
188
+ clearPriorityFilter() {
189
+ this.clearFilter('priority')
190
+ return this
191
+ }
192
+
157
193
  // ============================================
158
194
  // ENTITY-SPECIFIC ASSERTIONS
159
195
  // ============================================
@@ -171,6 +207,40 @@ export class TasksPOM extends DashboardEntityPOM {
171
207
  assertTaskNotInList(title: string) {
172
208
  return this.assertNotInList(title)
173
209
  }
210
+
211
+ /**
212
+ * Assert task has specific status in list
213
+ */
214
+ assertTaskStatus(title: string, status: string) {
215
+ cy.contains(this.selectors.rowGeneric, title)
216
+ .should('contain.text', status)
217
+ return this
218
+ }
219
+
220
+ /**
221
+ * Assert task has specific priority in list
222
+ */
223
+ assertTaskPriority(title: string, priority: string) {
224
+ cy.contains(this.selectors.rowGeneric, title)
225
+ .should('contain.text', priority)
226
+ return this
227
+ }
228
+
229
+ /**
230
+ * Assert task count in list
231
+ */
232
+ assertTaskCount(count: number) {
233
+ cy.get(this.selectors.rowGeneric).should('have.length', count)
234
+ return this
235
+ }
236
+
237
+ /**
238
+ * Assert no tasks in list
239
+ */
240
+ assertNoTasks() {
241
+ cy.get(this.selectors.rowGeneric).should('not.exist')
242
+ return this
243
+ }
174
244
  }
175
245
 
176
246
  export default TasksPOM
@@ -1,177 +1,20 @@
1
1
  /**
2
- * ApiInterceptor - Helper para waits determinísticos en Cypress
2
+ * ApiInterceptor - Re-export from @nextsparkjs/testing
3
3
  *
4
- * Reemplaza cy.wait(ms) poco fiables con waits basados en cy.intercept()
5
- * que esperan respuestas reales de la API.
4
+ * The ApiInterceptor class is provided by @nextsparkjs/testing.
5
+ * This file exists for backward compatibility and to allow
6
+ * theme-specific extensions if needed.
6
7
  *
7
- * Uso básico:
8
- * const api = new ApiInterceptor('customers')
9
- * api.setupCrudIntercepts()
10
- * cy.visit('/dashboard/customers')
11
- * api.waitForList()
8
+ * @example Basic usage:
9
+ * ```ts
10
+ * import { ApiInterceptor } from '../helpers/ApiInterceptor'
12
11
  *
13
- * Uso con ruta custom:
14
- * const api = new ApiInterceptor({
15
- * slug: 'categories',
16
- * customPath: '/api/v1/post-categories'
17
- * })
12
+ * const api = new ApiInterceptor('tasks')
13
+ * api.setupCrudIntercepts()
14
+ * cy.visit('/dashboard/tasks')
15
+ * api.waitForList()
16
+ * ```
18
17
  */
19
18
 
20
- export interface ApiInterceptorConfig {
21
- /** Entity slug - usado para generar aliases */
22
- slug: string
23
- /** Ruta API custom (ej: '/api/v1/post-categories') */
24
- customPath?: string
25
- }
26
-
27
- export class ApiInterceptor {
28
- private slug: string
29
- private endpoint: string
30
-
31
- constructor(slugOrConfig: string | ApiInterceptorConfig) {
32
- if (typeof slugOrConfig === 'string') {
33
- this.slug = slugOrConfig
34
- this.endpoint = `/api/v1/${slugOrConfig}`
35
- } else {
36
- this.slug = slugOrConfig.slug
37
- this.endpoint = slugOrConfig.customPath || `/api/v1/${slugOrConfig.slug}`
38
- }
39
- }
40
-
41
- // ============================================
42
- // ACCESSORS
43
- // ============================================
44
-
45
- /** Get the API endpoint path */
46
- get path(): string {
47
- return this.endpoint
48
- }
49
-
50
- /** Get the entity slug */
51
- get entitySlug(): string {
52
- return this.slug
53
- }
54
-
55
- /** Get alias names for all operations */
56
- get aliases() {
57
- return {
58
- list: `${this.slug}List`,
59
- create: `${this.slug}Create`,
60
- update: `${this.slug}Update`,
61
- delete: `${this.slug}Delete`
62
- }
63
- }
64
-
65
- // ============================================
66
- // INTERCEPT SETUP
67
- // ============================================
68
-
69
- /**
70
- * Setup intercepts for all CRUD operations
71
- * Call this BEFORE navigation in beforeEach or at test start
72
- *
73
- * Note: We intercept both PUT and PATCH for updates since different
74
- * APIs may use different HTTP methods for updates.
75
- */
76
- setupCrudIntercepts(): this {
77
- cy.intercept('GET', `${this.endpoint}*`).as(this.aliases.list)
78
- cy.intercept('POST', this.endpoint).as(this.aliases.create)
79
- // Intercept both PUT and PATCH for updates (APIs may use either)
80
- cy.intercept('PUT', `${this.endpoint}/*`).as(this.aliases.update)
81
- cy.intercept('PATCH', `${this.endpoint}/*`).as(`${this.aliases.update}Patch`)
82
- cy.intercept('DELETE', `${this.endpoint}/*`).as(this.aliases.delete)
83
- return this
84
- }
85
-
86
- /**
87
- * Setup only list + create intercepts
88
- * Useful for list pages with inline create
89
- */
90
- setupListIntercepts(): this {
91
- cy.intercept('GET', `${this.endpoint}*`).as(this.aliases.list)
92
- cy.intercept('POST', this.endpoint).as(this.aliases.create)
93
- return this
94
- }
95
-
96
- // ============================================
97
- // WAIT METHODS
98
- // ============================================
99
-
100
- /**
101
- * Wait for list response (GET)
102
- * Use after navigation or after mutations to wait for refresh
103
- */
104
- waitForList(timeout = 10000): Cypress.Chainable {
105
- return cy.wait(`@${this.aliases.list}`, { timeout })
106
- }
107
-
108
- /**
109
- * Wait for create response (POST) and validate success status
110
- */
111
- waitForCreate(timeout = 10000): Cypress.Chainable {
112
- return cy.wait(`@${this.aliases.create}`, { timeout })
113
- .its('response.statusCode')
114
- .should('be.oneOf', [200, 201])
115
- }
116
-
117
- /**
118
- * Wait for update response (PATCH or PUT) and validate success status
119
- * Waits for PATCH first (more common), falls back to PUT
120
- */
121
- waitForUpdate(timeout = 10000): Cypress.Chainable {
122
- // Try PATCH first (more common in modern APIs), fall back to PUT
123
- return cy.wait(`@${this.aliases.update}Patch`, { timeout })
124
- .its('response.statusCode')
125
- .should('be.oneOf', [200, 201])
126
- }
127
-
128
- /**
129
- * Wait for delete response (DELETE) and validate success status
130
- */
131
- waitForDelete(timeout = 10000): Cypress.Chainable {
132
- return cy.wait(`@${this.aliases.delete}`, { timeout })
133
- .its('response.statusCode')
134
- .should('be.oneOf', [200, 204])
135
- }
136
-
137
- // ============================================
138
- // CONVENIENCE METHODS
139
- // ============================================
140
-
141
- /**
142
- * Wait for list refresh (alias for waitForList)
143
- * Semantic name for use after create/update/delete
144
- */
145
- waitForRefresh(timeout = 10000): Cypress.Chainable {
146
- return this.waitForList(timeout)
147
- }
148
-
149
- /**
150
- * Wait for create + list refresh
151
- * Common pattern: create entity, wait for success, wait for list to refresh
152
- */
153
- waitForCreateAndRefresh(timeout = 10000): Cypress.Chainable {
154
- this.waitForCreate(timeout)
155
- return this.waitForList(timeout)
156
- }
157
-
158
- /**
159
- * Wait for update + list refresh
160
- * Common pattern: update entity, wait for success, wait for list to refresh
161
- */
162
- waitForUpdateAndRefresh(timeout = 10000): Cypress.Chainable {
163
- this.waitForUpdate(timeout)
164
- return this.waitForList(timeout)
165
- }
166
-
167
- /**
168
- * Wait for delete + list refresh
169
- * Common pattern: delete entity, wait for success, wait for list to refresh
170
- */
171
- waitForDeleteAndRefresh(timeout = 10000): Cypress.Chainable {
172
- this.waitForDelete(timeout)
173
- return this.waitForList(timeout)
174
- }
175
- }
176
-
177
- export default ApiInterceptor
19
+ // Re-export from testing package
20
+ export { ApiInterceptor, type ApiInterceptorConfig } from '@nextsparkjs/testing/helpers'
@@ -10,8 +10,11 @@
10
10
  import { defineConfig } from 'cypress'
11
11
  import path from 'path'
12
12
  import fs from 'fs'
13
+ import { fileURLToPath } from 'url'
13
14
 
14
- // __dirname works natively with CommonJS module resolution (tsconfig.cypress.json)
15
+ // ESM-compatible __dirname
16
+ const __filename = fileURLToPath(import.meta.url)
17
+ const __dirname = path.dirname(__filename)
15
18
 
16
19
  // Paths relative to this config file
17
20
  const themeRoot = path.resolve(__dirname, '..')
@@ -52,22 +55,14 @@ export default defineConfig({
52
55
  // Base URL for the application
53
56
  baseUrl: `http://localhost:${port}`,
54
57
 
55
- // Spec patterns: theme tests (core tests only in monorepo)
56
- specPattern: isNpmMode
57
- ? [
58
- // npm mode: only theme tests
59
- path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
60
- ]
61
- : [
62
- // Monorepo: core tests + theme tests
63
- path.join(projectRoot, 'packages/core/tests/cypress/e2e/core/**/*.cy.{js,ts}'),
64
- path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
65
- ],
66
-
67
- // Support file (theme-local in npm mode, core in monorepo)
68
- supportFile: isNpmMode
69
- ? path.join(__dirname, 'cypress/support/e2e.ts')
70
- : path.join(projectRoot, 'packages/core/tests/cypress/support/e2e.ts'),
58
+ // Spec patterns: theme tests only
59
+ // Note: core e2e tests were removed as they were empty
60
+ specPattern: [
61
+ path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
62
+ ],
63
+
64
+ // Support file (always theme-local)
65
+ supportFile: path.join(__dirname, 'cypress/support/e2e.ts'),
71
66
 
72
67
  // Fixtures folder (theme-specific)
73
68
  fixturesFolder: path.join(__dirname, 'cypress/fixtures'),
@@ -1,155 +0,0 @@
1
- {
2
- "title": "Sector 7 - SuperAdmin Teams View",
3
- "description": "Tutorial que muestra cómo un super administrador puede acceder a Sector 7 y visualizar todos los equipos del sistema.",
4
- "language": "es",
5
- "estimatedDuration": "1-1:30 minutos",
6
- "targetAudience": "Usuarios finales con rol superadmin",
7
- "chapters": [
8
- {
9
- "id": "intro",
10
- "title": "Introducción",
11
- "narrations": [
12
- {
13
- "step": 1,
14
- "text": "Bienvenido a Sector 7, el área exclusiva para super administradores.",
15
- "action": "none",
16
- "estimatedSeconds": 4
17
- },
18
- {
19
- "step": 2,
20
- "text": "Hoy veremos cómo acceder y visualizar todos los equipos del sistema.",
21
- "action": "none",
22
- "estimatedSeconds": 4
23
- }
24
- ]
25
- },
26
- {
27
- "id": "login",
28
- "title": "Login como SuperAdmin",
29
- "narrations": [
30
- {
31
- "step": 3,
32
- "text": "Iniciamos sesión con las credenciales de superadmin.",
33
- "action": "visit-login",
34
- "estimatedSeconds": 3
35
- },
36
- {
37
- "step": 4,
38
- "text": "Usamos el email del superadmin de prueba.",
39
- "action": "fill-email",
40
- "estimatedSeconds": 4
41
- },
42
- {
43
- "step": 5,
44
- "text": "Ingresamos la contraseña y enviamos.",
45
- "action": "fill-password-submit",
46
- "estimatedSeconds": 5
47
- },
48
- {
49
- "step": 6,
50
- "text": "Acceso concedido. Observa el icono de Sector 7 en la barra superior.",
51
- "action": "highlight-sector7-icon",
52
- "estimatedSeconds": 5
53
- }
54
- ]
55
- },
56
- {
57
- "id": "navigation",
58
- "title": "Navegación a Sector 7",
59
- "narrations": [
60
- {
61
- "step": 7,
62
- "text": "Hacemos clic para ingresar al área restringida.",
63
- "action": "click-sector7",
64
- "estimatedSeconds": 3
65
- },
66
- {
67
- "step": 8,
68
- "text": "Este es el panel de control del super administrador.",
69
- "action": "show-dashboard",
70
- "estimatedSeconds": 5
71
- },
72
- {
73
- "step": 9,
74
- "text": "En el menú lateral vemos las opciones disponibles.",
75
- "action": "highlight-teams-nav",
76
- "estimatedSeconds": 4
77
- }
78
- ]
79
- },
80
- {
81
- "id": "teams-view",
82
- "title": "Visualización de Teams",
83
- "narrations": [
84
- {
85
- "step": 10,
86
- "text": "Navegamos a la sección de Equipos.",
87
- "action": "click-teams-nav",
88
- "estimatedSeconds": 3
89
- },
90
- {
91
- "step": 11,
92
- "text": "Aquí vemos todos los equipos del sistema con estadísticas.",
93
- "action": "show-stats",
94
- "estimatedSeconds": 5
95
- },
96
- {
97
- "step": 12,
98
- "text": "La pestaña Work Teams muestra equipos colaborativos.",
99
- "action": "highlight-work-tab",
100
- "estimatedSeconds": 4
101
- },
102
- {
103
- "step": 13,
104
- "text": "También podemos ver los equipos personales de cada usuario.",
105
- "action": "click-personal-tab",
106
- "estimatedSeconds": 5
107
- },
108
- {
109
- "step": 14,
110
- "text": "El buscador permite filtrar equipos por nombre o email del propietario.",
111
- "action": "highlight-search",
112
- "estimatedSeconds": 4
113
- }
114
- ]
115
- },
116
- {
117
- "id": "conclusion",
118
- "title": "Conclusión",
119
- "narrations": [
120
- {
121
- "step": 15,
122
- "text": "Así funciona Sector 7: acceso exclusivo a la gestión global del sistema.",
123
- "action": "none",
124
- "estimatedSeconds": 5
125
- },
126
- {
127
- "step": 16,
128
- "text": "Gracias por ver este tutorial.",
129
- "action": "none",
130
- "estimatedSeconds": 3
131
- }
132
- ]
133
- }
134
- ],
135
- "totalSteps": 16,
136
- "technicalNotes": {
137
- "users": {
138
- "superadmin": "superadmin@cypress.com",
139
- "password": "Pandora1234"
140
- },
141
- "routes": [
142
- "/login",
143
- "/dashboard",
144
- "/sector7",
145
- "/sector7/teams"
146
- ],
147
- "selectors": {
148
- "topnavSector7": "[data-cy='topnav-sector7']",
149
- "sector7NavTeams": "[data-cy='sector7-nav-teams']",
150
- "teamsSearchInput": "[data-cy='teams-search-input']",
151
- "workTeamsTab": "[value='work']",
152
- "personalTeamsTab": "[value='personal']"
153
- }
154
- }
155
- }