@nextsparkjs/theme-default 0.1.0-beta.44 → 0.1.0-beta.45
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/components/ai-chat/ChatPanel.tsx +7 -7
- package/components/ai-chat/Message.tsx +2 -2
- package/components/ai-chat/MessageInput.tsx +3 -3
- package/components/ai-chat/MessageList.tsx +3 -3
- package/components/ai-chat/TypingIndicator.tsx +2 -2
- package/entities/customers/api/docs.md +107 -0
- package/entities/customers/api/presets.ts +80 -0
- package/entities/pages/api/docs.md +114 -0
- package/entities/pages/api/presets.ts +72 -0
- package/entities/posts/api/docs.md +120 -0
- package/entities/posts/api/presets.ts +74 -0
- package/entities/tasks/api/docs.md +126 -0
- package/entities/tasks/api/presets.ts +84 -0
- package/lib/selectors.ts +2 -2
- package/messages/de/admin.json +45 -0
- package/messages/en/admin.json +45 -0
- package/messages/es/admin.json +45 -0
- package/messages/fr/admin.json +45 -0
- package/messages/it/admin.json +45 -0
- package/messages/pt/admin.json +45 -0
- package/package.json +3 -3
- package/styles/globals.css +24 -0
- package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +491 -0
- package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +475 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
- package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +14 -14
- package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +3 -3
- package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +38 -73
- package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +21 -42
- package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +117 -38
- package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +35 -12
- package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +50 -59
- package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +15 -23
- package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +395 -155
- package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +795 -174
- package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
- package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
- package/tests/cypress/src/core/BlockEditorBasePOM.ts +269 -99
- package/tests/cypress/src/core/DashboardEntityPOM.ts +1 -1
- package/tests/cypress/src/features/DashboardPOM.ts +49 -28
- package/tests/cypress/src/features/PageBuilderPOM.ts +20 -0
- package/tests/cypress/src/features/SettingsPOM.ts +511 -166
- package/tests/cypress/src/features/SuperadminPOM.ts +679 -159
- package/tests/cypress/src/features/index.ts +10 -10
- package/tests/cypress/e2e/_utils/selectors/pages-editor.bdd.md +0 -207
- package/tests/cypress/e2e/_utils/selectors/pages-editor.cy.ts +0 -211
- package/tests/cypress/e2e/_utils/selectors/posts-editor.bdd.md +0 -184
- package/tests/cypress/e2e/_utils/selectors/posts-editor.cy.ts +0 -350
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* UI Selectors Validation: Tasks Entity
|
|
3
3
|
*
|
|
4
|
-
* This test validates that
|
|
5
|
-
*
|
|
4
|
+
* This test validates that all entity selectors exist in the DOM.
|
|
5
|
+
* Organized by the 6 first-level keys in ENTITIES_SELECTORS:
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - Test before migrating existing tests to new architecture
|
|
7
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
8
|
+
* STRUCTURE (matches entities.selectors.ts)
|
|
9
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
11
|
+
* 1. PAGE - Page-level container
|
|
12
|
+
* 2. LIST - List view (search, filters, table, pagination, bulk, confirm)
|
|
13
|
+
* 3. HEADER - Entity detail header (view/edit/create modes)
|
|
14
|
+
* 4. DETAIL - Detail view container
|
|
15
|
+
* 5. FORM - Form container, fields, and actions
|
|
16
|
+
* 6. CHILD - Child entity management (not applicable for tasks)
|
|
17
|
+
*
|
|
18
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
19
|
+
*
|
|
20
|
+
* Test IDs:
|
|
21
|
+
* - SEL_TASK_PAGE_001: Page Container
|
|
22
|
+
* - SEL_TASK_LIST_001: Search Selectors
|
|
23
|
+
* - SEL_TASK_LIST_002: Table Structure Selectors
|
|
24
|
+
* - SEL_TASK_LIST_003: Row Dynamic Selectors
|
|
25
|
+
* - SEL_TASK_LIST_004: Pagination Selectors
|
|
26
|
+
* - SEL_TASK_LIST_005: Filter Selectors
|
|
27
|
+
* - SEL_TASK_LIST_006: Bulk Action Selectors
|
|
28
|
+
* - SEL_TASK_LIST_007: Confirm Dialog Selectors
|
|
29
|
+
* - SEL_TASK_HEADER_001: Header Selectors (all modes)
|
|
30
|
+
* - SEL_TASK_DETAIL_001: Detail Container
|
|
31
|
+
* - SEL_TASK_FORM_001: Form Selectors
|
|
32
|
+
*
|
|
33
|
+
* POM: TasksPOM extends DashboardEntityPOM
|
|
34
|
+
* Selectors: ENTITIES_SELECTORS (6 first-level keys)
|
|
15
35
|
*/
|
|
16
36
|
|
|
17
37
|
import { TasksPOM } from '../../../src/entities/TasksPOM'
|
|
18
38
|
import { loginAsDefaultOwner } from '../../../src/session-helpers'
|
|
19
39
|
|
|
20
|
-
describe('Tasks Entity Selectors Validation', { tags: ['@ui-selectors', '@tasks'] }, () => {
|
|
40
|
+
describe('Tasks Entity Selectors Validation', { tags: ['@ui-selectors', '@entities', '@tasks'] }, () => {
|
|
21
41
|
const tasks = TasksPOM.create()
|
|
22
42
|
|
|
23
43
|
beforeEach(() => {
|
|
@@ -25,219 +45,820 @@ describe('Tasks Entity Selectors Validation', { tags: ['@ui-selectors', '@tasks'
|
|
|
25
45
|
loginAsDefaultOwner()
|
|
26
46
|
})
|
|
27
47
|
|
|
28
|
-
|
|
48
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
49
|
+
// 1. PAGE - Page-level container
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
51
|
+
describe('SEL_TASK_PAGE_001: Page Container', { tags: '@SEL_TASK_PAGE_001' }, () => {
|
|
29
52
|
beforeEach(() => {
|
|
30
53
|
tasks.visitList()
|
|
31
54
|
tasks.waitForList()
|
|
32
55
|
})
|
|
33
56
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
cy.get(tasks.selectors.addButton).should('exist')
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should find search input', () => {
|
|
47
|
-
cy.get(tasks.selectors.search).should('exist')
|
|
57
|
+
/**
|
|
58
|
+
* Selector: entities.page.container
|
|
59
|
+
* POM: tasks.selectors.page
|
|
60
|
+
* data-cy: tasks-page
|
|
61
|
+
*/
|
|
62
|
+
it.skip('should find page container (not implemented in EntityList)', () => {
|
|
63
|
+
// NOTE: page.container selector not currently implemented in EntityList component
|
|
64
|
+
// The page container would wrap the entire entity list view
|
|
65
|
+
cy.get(tasks.selectors.page).should('exist')
|
|
48
66
|
})
|
|
67
|
+
})
|
|
49
68
|
|
|
50
|
-
|
|
51
|
-
|
|
69
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
70
|
+
// 2. LIST - List view selectors
|
|
71
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
72
|
+
describe('LIST Selectors', { tags: '@entities-list' }, () => {
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// 2.1 SEARCH - Search input and controls
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
describe('SEL_TASK_LIST_001: Search Selectors', { tags: '@SEL_TASK_LIST_001' }, () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
tasks.visitList()
|
|
79
|
+
tasks.waitForList()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Selector: entities.list.search.input
|
|
84
|
+
* POM: tasks.selectors.search
|
|
85
|
+
* data-cy: tasks-search-input
|
|
86
|
+
*/
|
|
87
|
+
it('should find search input', () => {
|
|
88
|
+
cy.get(tasks.selectors.search).should('exist')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Selector: entities.list.search.container
|
|
93
|
+
* POM: tasks.selectors.searchContainer
|
|
94
|
+
* data-cy: tasks-search
|
|
95
|
+
*/
|
|
96
|
+
it('should find search container', () => {
|
|
97
|
+
cy.get(tasks.selectors.searchContainer).should('exist')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Selector: entities.list.search.clear
|
|
102
|
+
* POM: tasks.selectors.searchClear
|
|
103
|
+
* data-cy: tasks-search-clear
|
|
104
|
+
*/
|
|
105
|
+
it.skip('should find search clear button (not implemented)', () => {
|
|
106
|
+
// NOTE: searchClear selector not currently implemented in EntityTable
|
|
107
|
+
cy.get(tasks.selectors.searchClear).should('exist')
|
|
108
|
+
})
|
|
52
109
|
})
|
|
53
110
|
|
|
54
|
-
|
|
55
|
-
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// 2.2 TABLE - Table structure selectors
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
describe('SEL_TASK_LIST_002: Table Structure Selectors', { tags: '@SEL_TASK_LIST_002' }, () => {
|
|
115
|
+
beforeEach(() => {
|
|
116
|
+
tasks.visitList()
|
|
117
|
+
tasks.waitForList()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Selector: entities.list.table.container
|
|
122
|
+
* POM: tasks.selectors.tableContainer
|
|
123
|
+
* data-cy: tasks-table-container
|
|
124
|
+
*/
|
|
125
|
+
it('should find table container', () => {
|
|
126
|
+
cy.get(tasks.selectors.tableContainer).should('exist')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Selector: entities.list.table.element
|
|
131
|
+
* POM: tasks.selectors.table
|
|
132
|
+
* data-cy: tasks-table
|
|
133
|
+
* NOTE: Table element only renders when there's data (not empty state)
|
|
134
|
+
*/
|
|
135
|
+
it('should find table element (requires data)', () => {
|
|
136
|
+
// Skip if no rows exist (empty state)
|
|
137
|
+
cy.get('body').then(($body) => {
|
|
138
|
+
if ($body.find(tasks.selectors.rowGeneric).length > 0) {
|
|
139
|
+
cy.get(tasks.selectors.table).should('exist')
|
|
140
|
+
} else {
|
|
141
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Selector: entities.list.addButton
|
|
148
|
+
* POM: tasks.selectors.addButton
|
|
149
|
+
* data-cy: tasks-add
|
|
150
|
+
*/
|
|
151
|
+
it('should find add button', () => {
|
|
152
|
+
cy.get(tasks.selectors.addButton).should('exist')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Selector: entities.list.table.selectAll
|
|
157
|
+
* POM: tasks.selectors.selectAll
|
|
158
|
+
* data-cy: tasks-select-all
|
|
159
|
+
* NOTE: SelectAll checkbox only renders when there's data and selectable=true
|
|
160
|
+
*/
|
|
161
|
+
it('should find select all checkbox (requires data)', () => {
|
|
162
|
+
// Skip if no rows exist (empty state)
|
|
163
|
+
cy.get('body').then(($body) => {
|
|
164
|
+
if ($body.find(tasks.selectors.rowGeneric).length > 0) {
|
|
165
|
+
cy.get(tasks.selectors.selectAll).should('exist')
|
|
166
|
+
} else {
|
|
167
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Selector: entities.list.selectionCount
|
|
174
|
+
* POM: tasks.selectors.selectionCount
|
|
175
|
+
* data-cy: tasks-selection-count
|
|
176
|
+
* NOTE: Only visible when items are selected
|
|
177
|
+
*/
|
|
178
|
+
it('should find selection count after selecting items (requires data)', () => {
|
|
179
|
+
cy.get('body').then(($body) => {
|
|
180
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
181
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Select the first row to make selectionCount appear
|
|
186
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
187
|
+
.first()
|
|
188
|
+
.invoke('attr', 'data-cy')
|
|
189
|
+
.then((dataCy) => {
|
|
190
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
191
|
+
cy.get(tasks.selectors.rowSelect(id)).click()
|
|
192
|
+
|
|
193
|
+
// Now selectionCount should be visible
|
|
194
|
+
cy.get(tasks.selectors.selectionCount).should('exist')
|
|
195
|
+
|
|
196
|
+
// Clear selection
|
|
197
|
+
cy.get(tasks.selectors.rowSelect(id)).click()
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
})
|
|
56
201
|
})
|
|
57
202
|
|
|
58
|
-
|
|
59
|
-
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// 2.3 ROW - Dynamic row selectors
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
describe('SEL_TASK_LIST_003: Row Dynamic Selectors', { tags: '@SEL_TASK_LIST_003' }, () => {
|
|
207
|
+
beforeEach(() => {
|
|
208
|
+
tasks.visitList()
|
|
209
|
+
tasks.waitForList()
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Selector: entities.list.table.row.element (dynamic)
|
|
214
|
+
* POM: tasks.selectors.row(id)
|
|
215
|
+
* data-cy: tasks-row-{id}
|
|
216
|
+
* NOTE: Rows only render when there's data
|
|
217
|
+
*/
|
|
218
|
+
it('should find row elements with rowGeneric pattern (requires data)', () => {
|
|
219
|
+
cy.get('body').then(($body) => {
|
|
220
|
+
if ($body.find(tasks.selectors.rowGeneric).length > 0) {
|
|
221
|
+
cy.get(tasks.selectors.rowGeneric).should('have.length.at.least', 1)
|
|
222
|
+
} else {
|
|
223
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Tests dynamic selectors: row, rowSelect, rowMenu
|
|
230
|
+
* NOTE: Requires data to exist
|
|
231
|
+
*/
|
|
232
|
+
it('should find row with dynamic ID selectors (requires data)', () => {
|
|
233
|
+
cy.get('body').then(($body) => {
|
|
234
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
235
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
240
|
+
.first()
|
|
241
|
+
.invoke('attr', 'data-cy')
|
|
242
|
+
.then((dataCy) => {
|
|
243
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
244
|
+
expect(id).to.not.be.empty
|
|
245
|
+
|
|
246
|
+
// Selector: entities.list.table.row.element
|
|
247
|
+
cy.get(tasks.selectors.row(id)).should('exist')
|
|
248
|
+
|
|
249
|
+
// Selector: entities.list.table.row.checkbox
|
|
250
|
+
cy.get(tasks.selectors.rowSelect(id)).should('exist')
|
|
251
|
+
|
|
252
|
+
// Selector: entities.list.table.row.menu
|
|
253
|
+
cy.get(tasks.selectors.rowMenu(id)).should('exist')
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Selector: entities.list.table.row.action (dynamic)
|
|
260
|
+
* POM: tasks.selectors.rowAction(action, id)
|
|
261
|
+
* data-cy: tasks-action-{action}-{id}
|
|
262
|
+
* NOTE: Requires data to exist
|
|
263
|
+
*/
|
|
264
|
+
it('should find row action selectors in dropdown (requires data)', () => {
|
|
265
|
+
cy.get('body').then(($body) => {
|
|
266
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
267
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
272
|
+
.first()
|
|
273
|
+
.invoke('attr', 'data-cy')
|
|
274
|
+
.then((dataCy) => {
|
|
275
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
276
|
+
|
|
277
|
+
// Open row menu
|
|
278
|
+
cy.get(tasks.selectors.rowMenu(id)).click()
|
|
279
|
+
|
|
280
|
+
// Check action selectors exist
|
|
281
|
+
cy.get(tasks.selectors.rowAction('view', id)).should('exist')
|
|
282
|
+
cy.get(tasks.selectors.rowAction('edit', id)).should('exist')
|
|
283
|
+
cy.get(tasks.selectors.rowAction('delete', id)).should('exist')
|
|
284
|
+
|
|
285
|
+
// Close menu by pressing Escape
|
|
286
|
+
cy.get('body').type('{esc}')
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
})
|
|
60
290
|
})
|
|
61
291
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
293
|
+
// 2.4 PAGINATION - Pagination controls
|
|
294
|
+
// NOTE: Pagination only renders when there's data (not in empty state)
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
describe('SEL_TASK_LIST_004: Pagination Selectors', { tags: '@SEL_TASK_LIST_004' }, () => {
|
|
297
|
+
beforeEach(() => {
|
|
298
|
+
tasks.visitList()
|
|
299
|
+
tasks.waitForList()
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Selector: entities.list.pagination.container
|
|
304
|
+
* POM: tasks.selectors.pagination
|
|
305
|
+
* data-cy: tasks-pagination
|
|
306
|
+
* NOTE: Requires data to exist
|
|
307
|
+
*/
|
|
308
|
+
it('should find pagination container (requires data)', () => {
|
|
309
|
+
cy.get('body').then(($body) => {
|
|
310
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
311
|
+
cy.log('⚠️ Skipping: No data - pagination not rendered in empty state')
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
cy.get(tasks.selectors.pagination).should('exist')
|
|
315
|
+
})
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Selector: entities.list.pagination.info
|
|
320
|
+
* POM: tasks.selectors.pageInfo
|
|
321
|
+
* data-cy: tasks-page-info
|
|
322
|
+
* NOTE: Requires data to exist
|
|
323
|
+
*/
|
|
324
|
+
it('should find page info (requires data)', () => {
|
|
325
|
+
cy.get('body').then(($body) => {
|
|
326
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
327
|
+
cy.log('⚠️ Skipping: No data - pagination not rendered in empty state')
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
cy.get(tasks.selectors.pageInfo).should('exist')
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Selector: entities.list.pagination.pageSize
|
|
336
|
+
* POM: tasks.selectors.pageSize
|
|
337
|
+
* data-cy: tasks-page-size
|
|
338
|
+
* NOTE: Requires data to exist
|
|
339
|
+
*/
|
|
340
|
+
it('should find page size selector (requires data)', () => {
|
|
341
|
+
cy.get('body').then(($body) => {
|
|
342
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
343
|
+
cy.log('⚠️ Skipping: No data - pagination not rendered in empty state')
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
cy.get(tasks.selectors.pageSize).should('exist')
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Selectors: entities.list.pagination.{first,prev,next,last}
|
|
352
|
+
* NOTE: Requires data to exist
|
|
353
|
+
*/
|
|
354
|
+
it('should find pagination navigation controls (requires data)', () => {
|
|
355
|
+
cy.get('body').then(($body) => {
|
|
356
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
357
|
+
cy.log('⚠️ Skipping: No data - pagination not rendered in empty state')
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
cy.get(tasks.selectors.pageFirst).should('exist')
|
|
361
|
+
cy.get(tasks.selectors.pagePrev).should('exist')
|
|
362
|
+
cy.get(tasks.selectors.pageNext).should('exist')
|
|
363
|
+
cy.get(tasks.selectors.pageLast).should('exist')
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Selector: entities.list.pagination.pageSizeOption (dynamic)
|
|
369
|
+
* POM: tasks.selectors.pageSizeOption(size)
|
|
370
|
+
* data-cy: tasks-page-size-{size}
|
|
371
|
+
* NOTE: Requires data to exist
|
|
372
|
+
*/
|
|
373
|
+
it('should find page size options when dropdown opened (requires data)', () => {
|
|
374
|
+
cy.get('body').then(($body) => {
|
|
375
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
376
|
+
cy.log('⚠️ Skipping: No data - pagination not rendered in empty state')
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
cy.get(tasks.selectors.pageSize).click()
|
|
380
|
+
cy.get(tasks.selectors.pageSizeOption('10')).should('exist')
|
|
381
|
+
cy.get(tasks.selectors.pageSizeOption('20')).should('exist')
|
|
382
|
+
cy.get('body').type('{esc}')
|
|
383
|
+
})
|
|
384
|
+
})
|
|
67
385
|
})
|
|
68
386
|
|
|
69
|
-
|
|
70
|
-
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
// 2.5 FILTERS - Filter controls
|
|
389
|
+
// ---------------------------------------------------------------------------
|
|
390
|
+
describe('SEL_TASK_LIST_005: Filter Selectors', { tags: '@SEL_TASK_LIST_005' }, () => {
|
|
391
|
+
beforeEach(() => {
|
|
392
|
+
tasks.visitList()
|
|
393
|
+
tasks.waitForList()
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Selector: entities.list.filters.trigger
|
|
398
|
+
* POM: tasks.selectors.filterTrigger(field)
|
|
399
|
+
* data-cy: tasks-filter-{field}
|
|
400
|
+
*/
|
|
401
|
+
it('should find status filter trigger', () => {
|
|
402
|
+
cy.get(tasks.selectors.filterTrigger('status')).should('exist')
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
it('should find priority filter trigger', () => {
|
|
406
|
+
cy.get(tasks.selectors.filterTrigger('priority')).should('exist')
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Selector: entities.list.filters.content
|
|
411
|
+
* POM: tasks.selectors.filterContent(field)
|
|
412
|
+
* data-cy: tasks-filter-{field}-content
|
|
413
|
+
*/
|
|
414
|
+
it('should find filter content when opened', () => {
|
|
415
|
+
tasks.openFilter('status')
|
|
416
|
+
cy.get(tasks.selectors.filterContent('status')).should('be.visible')
|
|
417
|
+
cy.get('body').type('{esc}')
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Selector: entities.list.filters.option
|
|
422
|
+
* POM: tasks.selectors.filterOption(field, value)
|
|
423
|
+
* data-cy: tasks-filter-{field}-{value}
|
|
424
|
+
* NOTE: Status values are: todo, in-progress, review, done, blocked
|
|
425
|
+
*/
|
|
426
|
+
it('should find filter options', () => {
|
|
427
|
+
tasks.openFilter('status')
|
|
428
|
+
cy.get(tasks.selectors.filterOption('status', 'todo')).should('exist')
|
|
429
|
+
cy.get('body').type('{esc}')
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Selector: entities.list.filters.clearAll
|
|
434
|
+
* POM: tasks.selectors.filterClearAll(field)
|
|
435
|
+
* data-cy: tasks-filter-{field}-clear-all
|
|
436
|
+
* NOTE: Clear button only appears when >1 option is selected
|
|
437
|
+
*/
|
|
438
|
+
it('should find filter clear button when multiple options selected', () => {
|
|
439
|
+
// Open status filter
|
|
440
|
+
tasks.openFilter('status')
|
|
441
|
+
|
|
442
|
+
// Select 2 options to make clear button appear
|
|
443
|
+
cy.get(tasks.selectors.filterOption('status', 'todo')).click()
|
|
444
|
+
cy.get(tasks.selectors.filterOption('status', 'in-progress')).click()
|
|
445
|
+
|
|
446
|
+
// Close dropdown
|
|
447
|
+
cy.get('body').type('{esc}')
|
|
448
|
+
|
|
449
|
+
// Clear button should now exist
|
|
450
|
+
cy.get(tasks.selectors.filterClearAll('status')).should('exist')
|
|
451
|
+
|
|
452
|
+
// Click to clear and verify it disappears
|
|
453
|
+
cy.get(tasks.selectors.filterClearAll('status')).click()
|
|
454
|
+
cy.get(tasks.selectors.filterClearAll('status')).should('not.exist')
|
|
455
|
+
})
|
|
71
456
|
})
|
|
72
457
|
|
|
73
|
-
|
|
74
|
-
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
// 2.6 BULK - Bulk action selectors
|
|
460
|
+
// ---------------------------------------------------------------------------
|
|
461
|
+
describe('SEL_TASK_LIST_006: Bulk Action Selectors', { tags: '@SEL_TASK_LIST_006' }, () => {
|
|
462
|
+
beforeEach(() => {
|
|
463
|
+
tasks.visitList()
|
|
464
|
+
tasks.waitForList()
|
|
465
|
+
})
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Selectors: entities.list.bulk.{bar,count,delete,clear}
|
|
469
|
+
* Bulk bar appears after selecting items
|
|
470
|
+
* NOTE: Requires data to exist
|
|
471
|
+
*/
|
|
472
|
+
it('should show bulk bar selectors after selecting rows (requires data)', () => {
|
|
473
|
+
cy.get('body').then(($body) => {
|
|
474
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
475
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
476
|
+
return
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
480
|
+
.first()
|
|
481
|
+
.invoke('attr', 'data-cy')
|
|
482
|
+
.then((dataCy) => {
|
|
483
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
484
|
+
cy.get(tasks.selectors.rowSelect(id)).click()
|
|
485
|
+
|
|
486
|
+
// Selector: entities.list.bulk.bar
|
|
487
|
+
cy.get(tasks.selectors.bulkBar).should('be.visible')
|
|
488
|
+
|
|
489
|
+
// Selector: entities.list.bulk.count
|
|
490
|
+
cy.get(tasks.selectors.bulkCount).should('exist')
|
|
491
|
+
|
|
492
|
+
// Selector: entities.list.bulk.deleteButton
|
|
493
|
+
cy.get(tasks.selectors.bulkDelete).should('exist')
|
|
494
|
+
|
|
495
|
+
// Selector: entities.list.bulk.clearButton
|
|
496
|
+
cy.get(tasks.selectors.bulkClear).should('exist')
|
|
497
|
+
})
|
|
498
|
+
})
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Selector: entities.list.bulk.statusButton
|
|
503
|
+
* POM: tasks.selectors.bulkStatus
|
|
504
|
+
* NOTE: enableChangeStatus not enabled in EntityListWrapper for tasks
|
|
505
|
+
*/
|
|
506
|
+
it.skip('should find bulk status button (enableChangeStatus not enabled)', () => {
|
|
507
|
+
cy.get(tasks.selectors.bulkStatus).should('exist')
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Selectors: entities.list.bulk.deleteDialog, deleteCancel, deleteConfirm
|
|
512
|
+
* NOTE: Requires data to exist
|
|
513
|
+
*/
|
|
514
|
+
it('should show bulk delete dialog selectors (requires data)', () => {
|
|
515
|
+
cy.get('body').then(($body) => {
|
|
516
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
517
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
522
|
+
.first()
|
|
523
|
+
.invoke('attr', 'data-cy')
|
|
524
|
+
.then((dataCy) => {
|
|
525
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
526
|
+
cy.get(tasks.selectors.rowSelect(id)).click()
|
|
527
|
+
|
|
528
|
+
// Open bulk delete dialog
|
|
529
|
+
cy.get(tasks.selectors.bulkDelete).click()
|
|
530
|
+
|
|
531
|
+
// Selector: entities.list.bulk.deleteDialog
|
|
532
|
+
cy.get(tasks.selectors.bulkDeleteDialog).should('be.visible')
|
|
533
|
+
|
|
534
|
+
// Selector: entities.list.bulk.deleteCancel
|
|
535
|
+
cy.get(tasks.selectors.bulkDeleteCancel).should('exist')
|
|
536
|
+
|
|
537
|
+
// Selector: entities.list.bulk.deleteConfirm
|
|
538
|
+
cy.get(tasks.selectors.bulkDeleteConfirm).should('exist')
|
|
539
|
+
|
|
540
|
+
// Cancel without deleting
|
|
541
|
+
cy.get(tasks.selectors.bulkDeleteCancel).click()
|
|
542
|
+
})
|
|
543
|
+
})
|
|
544
|
+
})
|
|
75
545
|
})
|
|
76
546
|
|
|
77
|
-
|
|
78
|
-
|
|
547
|
+
// ---------------------------------------------------------------------------
|
|
548
|
+
// 2.7 CONFIRM - Row action confirm dialogs
|
|
549
|
+
// ---------------------------------------------------------------------------
|
|
550
|
+
describe('SEL_TASK_LIST_007: Confirm Dialog Selectors', { tags: '@SEL_TASK_LIST_007' }, () => {
|
|
551
|
+
/**
|
|
552
|
+
* Selectors: entities.list.confirm.{dialog,cancel,action}
|
|
553
|
+
* Confirm dialog appears for row delete action
|
|
554
|
+
* NOTE: Requires data to exist
|
|
555
|
+
*/
|
|
556
|
+
it('should show confirm dialog selectors for row delete (requires data)', () => {
|
|
557
|
+
tasks.visitList()
|
|
558
|
+
tasks.waitForList()
|
|
559
|
+
|
|
560
|
+
cy.get('body').then(($body) => {
|
|
561
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
562
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
563
|
+
return
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
567
|
+
.first()
|
|
568
|
+
.invoke('attr', 'data-cy')
|
|
569
|
+
.then((dataCy) => {
|
|
570
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
571
|
+
|
|
572
|
+
// Open row menu and click delete
|
|
573
|
+
cy.get(tasks.selectors.rowMenu(id)).click()
|
|
574
|
+
cy.get(tasks.selectors.rowAction('delete', id)).click()
|
|
575
|
+
|
|
576
|
+
// Selector: entities.list.confirm.dialog
|
|
577
|
+
cy.get(tasks.selectors.confirmDialog).should('be.visible')
|
|
578
|
+
|
|
579
|
+
// Selector: entities.list.confirm.cancel
|
|
580
|
+
cy.get(tasks.selectors.confirmCancel).should('exist')
|
|
581
|
+
|
|
582
|
+
// Selector: entities.list.confirm.action
|
|
583
|
+
cy.get(tasks.selectors.confirmAction).should('exist')
|
|
584
|
+
|
|
585
|
+
// Cancel without deleting
|
|
586
|
+
cy.get(tasks.selectors.confirmCancel).click()
|
|
587
|
+
})
|
|
588
|
+
})
|
|
589
|
+
})
|
|
79
590
|
})
|
|
80
591
|
})
|
|
81
592
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
593
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
594
|
+
// 3. HEADER - Entity detail header
|
|
595
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
596
|
+
describe('SEL_TASK_HEADER_001: Header Selectors', { tags: '@SEL_TASK_HEADER_001' }, () => {
|
|
597
|
+
/**
|
|
598
|
+
* Header selectors for CREATE mode
|
|
599
|
+
* Selectors: entities.header.container (mode: create), backButton
|
|
600
|
+
*/
|
|
601
|
+
describe('Create Mode', () => {
|
|
602
|
+
beforeEach(() => {
|
|
603
|
+
tasks.visitCreate()
|
|
604
|
+
tasks.waitForForm()
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Selector: entities.header.container (mode: create)
|
|
609
|
+
* POM: tasks.selectors.createHeader
|
|
610
|
+
* data-cy: tasks-create-header
|
|
611
|
+
*/
|
|
612
|
+
it('should find create header', () => {
|
|
613
|
+
cy.get(tasks.selectors.createHeader).should('exist')
|
|
614
|
+
})
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Selector: entities.header.backButton
|
|
618
|
+
* POM: tasks.selectors.backButton
|
|
619
|
+
* data-cy: tasks-back
|
|
620
|
+
*/
|
|
621
|
+
it('should find back button', () => {
|
|
622
|
+
cy.get(tasks.selectors.backButton).should('exist')
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Selector: entities.header.title
|
|
627
|
+
* POM: tasks.selectors.title
|
|
628
|
+
* data-cy: tasks-header-title
|
|
629
|
+
*/
|
|
630
|
+
it('should find header title', () => {
|
|
631
|
+
cy.get(tasks.selectors.title).should('exist')
|
|
632
|
+
})
|
|
90
633
|
})
|
|
91
634
|
|
|
92
|
-
|
|
93
|
-
|
|
635
|
+
/**
|
|
636
|
+
* Header selectors for VIEW mode
|
|
637
|
+
* Selectors: entities.header.container (mode: view), backButton, editButton, deleteButton
|
|
638
|
+
* NOTE: Requires data to exist
|
|
639
|
+
*/
|
|
640
|
+
describe('View Mode', () => {
|
|
641
|
+
it('should find view header selectors (requires data)', () => {
|
|
642
|
+
tasks.visitList()
|
|
643
|
+
tasks.waitForList()
|
|
644
|
+
|
|
645
|
+
cy.get('body').then(($body) => {
|
|
646
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
647
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
648
|
+
return
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
652
|
+
.first()
|
|
653
|
+
.invoke('attr', 'data-cy')
|
|
654
|
+
.then((dataCy) => {
|
|
655
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
656
|
+
|
|
657
|
+
tasks.visitDetail(id)
|
|
658
|
+
tasks.waitForDetail()
|
|
659
|
+
|
|
660
|
+
// Selector: entities.header.container (mode: view)
|
|
661
|
+
cy.get(tasks.selectors.viewHeader).should('exist')
|
|
662
|
+
|
|
663
|
+
// Selector: entities.header.backButton
|
|
664
|
+
cy.get(tasks.selectors.backButton).should('exist')
|
|
665
|
+
|
|
666
|
+
// Selector: entities.header.editButton
|
|
667
|
+
cy.get(tasks.selectors.editButton).should('exist')
|
|
668
|
+
|
|
669
|
+
// Selector: entities.header.deleteButton
|
|
670
|
+
cy.get(tasks.selectors.deleteButton).should('exist')
|
|
671
|
+
})
|
|
672
|
+
})
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Selectors: entities.header.{deleteDialog,deleteCancel,deleteConfirm}
|
|
677
|
+
* NOTE: Requires data to exist
|
|
678
|
+
*/
|
|
679
|
+
it('should find header delete dialog selectors (requires data)', () => {
|
|
680
|
+
tasks.visitList()
|
|
681
|
+
tasks.waitForList()
|
|
682
|
+
|
|
683
|
+
cy.get('body').then(($body) => {
|
|
684
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
685
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
686
|
+
return
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
690
|
+
.first()
|
|
691
|
+
.invoke('attr', 'data-cy')
|
|
692
|
+
.then((dataCy) => {
|
|
693
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
694
|
+
|
|
695
|
+
tasks.visitDetail(id)
|
|
696
|
+
tasks.waitForDetail()
|
|
697
|
+
|
|
698
|
+
// Click delete to open dialog
|
|
699
|
+
tasks.clickDelete()
|
|
700
|
+
|
|
701
|
+
// Selector: entities.header.deleteDialog
|
|
702
|
+
cy.get(tasks.selectors.deleteDialog).should('be.visible')
|
|
703
|
+
|
|
704
|
+
// Selector: entities.header.deleteConfirm
|
|
705
|
+
cy.get(tasks.selectors.deleteConfirm).should('exist')
|
|
706
|
+
|
|
707
|
+
// Selector: entities.header.deleteCancel
|
|
708
|
+
cy.get(tasks.selectors.deleteCancel).should('exist')
|
|
709
|
+
|
|
710
|
+
// Cancel without deleting
|
|
711
|
+
tasks.cancelDelete()
|
|
712
|
+
})
|
|
713
|
+
})
|
|
714
|
+
})
|
|
94
715
|
})
|
|
95
716
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
717
|
+
/**
|
|
718
|
+
* Header selectors for EDIT mode
|
|
719
|
+
* Selectors: entities.header.container (mode: edit), backButton
|
|
720
|
+
* NOTE: Requires data to exist
|
|
721
|
+
*/
|
|
722
|
+
describe('Edit Mode', () => {
|
|
723
|
+
it('should find edit header selectors (requires data)', () => {
|
|
724
|
+
tasks.visitList()
|
|
725
|
+
tasks.waitForList()
|
|
726
|
+
|
|
727
|
+
cy.get('body').then(($body) => {
|
|
728
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
729
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
730
|
+
return
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
734
|
+
.first()
|
|
735
|
+
.invoke('attr', 'data-cy')
|
|
736
|
+
.then((dataCy) => {
|
|
737
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
738
|
+
|
|
739
|
+
tasks.visitEdit(id)
|
|
740
|
+
tasks.waitForForm()
|
|
741
|
+
|
|
742
|
+
// Selector: entities.header.container (mode: edit)
|
|
743
|
+
cy.get(tasks.selectors.editHeader).should('exist')
|
|
744
|
+
|
|
745
|
+
// Selector: entities.header.backButton
|
|
746
|
+
cy.get(tasks.selectors.backButton).should('exist')
|
|
747
|
+
})
|
|
748
|
+
})
|
|
749
|
+
})
|
|
99
750
|
})
|
|
100
751
|
})
|
|
101
752
|
|
|
102
|
-
|
|
103
|
-
|
|
753
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
754
|
+
// 4. DETAIL - Detail view container
|
|
755
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
756
|
+
describe('SEL_TASK_DETAIL_001: Detail Container', { tags: '@SEL_TASK_DETAIL_001' }, () => {
|
|
757
|
+
/**
|
|
758
|
+
* Selector: entities.detail.container
|
|
759
|
+
* POM: tasks.selectors.detail
|
|
760
|
+
* data-cy: tasks-detail
|
|
761
|
+
* NOTE: Requires data to exist
|
|
762
|
+
*/
|
|
763
|
+
it('should find detail container (requires data)', () => {
|
|
104
764
|
tasks.visitList()
|
|
105
765
|
tasks.waitForList()
|
|
106
|
-
})
|
|
107
766
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.then((dataCy) => {
|
|
114
|
-
// Extract ID from data-cy="tasks-row-{id}"
|
|
115
|
-
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
116
|
-
expect(id).to.not.be.empty
|
|
767
|
+
cy.get('body').then(($body) => {
|
|
768
|
+
if ($body.find(tasks.selectors.rowGeneric).length === 0) {
|
|
769
|
+
cy.log('⚠️ Skipping: No data - empty state showing')
|
|
770
|
+
return
|
|
771
|
+
}
|
|
117
772
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
773
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
774
|
+
.first()
|
|
775
|
+
.invoke('attr', 'data-cy')
|
|
776
|
+
.then((dataCy) => {
|
|
777
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
778
|
+
|
|
779
|
+
tasks.visitDetail(id)
|
|
780
|
+
tasks.waitForDetail()
|
|
781
|
+
|
|
782
|
+
cy.get(tasks.selectors.detail).should('exist')
|
|
783
|
+
})
|
|
784
|
+
})
|
|
123
785
|
})
|
|
124
786
|
})
|
|
125
787
|
|
|
126
|
-
|
|
788
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
789
|
+
// 5. FORM - Form selectors
|
|
790
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
791
|
+
describe('SEL_TASK_FORM_001: Form Selectors', { tags: '@SEL_TASK_FORM_001' }, () => {
|
|
127
792
|
beforeEach(() => {
|
|
128
793
|
tasks.visitCreate()
|
|
129
794
|
tasks.waitForForm()
|
|
130
795
|
})
|
|
131
796
|
|
|
797
|
+
/**
|
|
798
|
+
* Selector: entities.form.container
|
|
799
|
+
* POM: tasks.selectors.form
|
|
800
|
+
* data-cy: tasks-form
|
|
801
|
+
*/
|
|
132
802
|
it('should find form container', () => {
|
|
133
803
|
cy.get(tasks.selectors.form).should('exist')
|
|
134
804
|
})
|
|
135
805
|
|
|
806
|
+
/**
|
|
807
|
+
* Selector: entities.form.submitButton
|
|
808
|
+
* POM: tasks.selectors.submitButton
|
|
809
|
+
* data-cy: tasks-form-submit
|
|
810
|
+
*/
|
|
136
811
|
it('should find submit button', () => {
|
|
137
812
|
cy.get(tasks.selectors.submitButton).should('exist')
|
|
138
813
|
})
|
|
139
814
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
cy.get(tasks.selectors.field('priority')).should('exist')
|
|
815
|
+
/**
|
|
816
|
+
* Selector: entities.form.field (dynamic)
|
|
817
|
+
* POM: tasks.selectors.field(name)
|
|
818
|
+
* data-cy: tasks-field-{name}
|
|
819
|
+
*/
|
|
820
|
+
describe('Field Selectors', () => {
|
|
821
|
+
it('should find title field', () => {
|
|
822
|
+
cy.get(tasks.selectors.field('title')).should('exist')
|
|
823
|
+
})
|
|
824
|
+
|
|
825
|
+
it('should find description field', () => {
|
|
826
|
+
cy.get(tasks.selectors.field('description')).should('exist')
|
|
827
|
+
})
|
|
828
|
+
|
|
829
|
+
it('should find status field', () => {
|
|
830
|
+
cy.get(tasks.selectors.field('status')).should('exist')
|
|
831
|
+
})
|
|
832
|
+
|
|
833
|
+
it('should find priority field', () => {
|
|
834
|
+
cy.get(tasks.selectors.field('priority')).should('exist')
|
|
835
|
+
})
|
|
162
836
|
})
|
|
163
837
|
})
|
|
164
838
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
describe('SEL_TASK_006: Bulk Actions Selectors', { tags: '@SEL_TASK_006' }, () => {
|
|
191
|
-
beforeEach(() => {
|
|
192
|
-
tasks.visitList()
|
|
193
|
-
tasks.waitForList()
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
it('should show bulk bar after selecting rows', () => {
|
|
197
|
-
// Select first row
|
|
198
|
-
cy.get(tasks.selectors.rowGeneric)
|
|
199
|
-
.first()
|
|
200
|
-
.invoke('attr', 'data-cy')
|
|
201
|
-
.then((dataCy) => {
|
|
202
|
-
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
203
|
-
cy.get(tasks.selectors.rowSelect(id)).click()
|
|
204
|
-
|
|
205
|
-
// Bulk bar should appear
|
|
206
|
-
cy.get(tasks.selectors.bulkBar).should('be.visible')
|
|
207
|
-
cy.get(tasks.selectors.bulkCount).should('exist')
|
|
208
|
-
cy.get(tasks.selectors.bulkDelete).should('exist')
|
|
209
|
-
cy.get(tasks.selectors.bulkClear).should('exist')
|
|
210
|
-
// Note: bulkStatus not tested - enableChangeStatus not enabled in EntityListWrapper
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
describe('SEL_TASK_007: Delete Dialog Selectors', { tags: '@SEL_TASK_007' }, () => {
|
|
216
|
-
it('should find delete dialog elements', () => {
|
|
217
|
-
// Navigate to a task detail
|
|
218
|
-
tasks.visitList()
|
|
219
|
-
tasks.waitForList()
|
|
220
|
-
|
|
221
|
-
cy.get(tasks.selectors.rowGeneric)
|
|
222
|
-
.first()
|
|
223
|
-
.invoke('attr', 'data-cy')
|
|
224
|
-
.then((dataCy) => {
|
|
225
|
-
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
226
|
-
|
|
227
|
-
tasks.visitDetail(id)
|
|
228
|
-
tasks.waitForDetail()
|
|
229
|
-
|
|
230
|
-
// Click delete to open dialog
|
|
231
|
-
tasks.clickDelete()
|
|
232
|
-
|
|
233
|
-
// Validate dialog selectors
|
|
234
|
-
cy.get(tasks.selectors.deleteDialog).should('be.visible')
|
|
235
|
-
cy.get(tasks.selectors.deleteConfirm).should('exist')
|
|
236
|
-
cy.get(tasks.selectors.deleteCancel).should('exist')
|
|
237
|
-
|
|
238
|
-
// Close without deleting
|
|
239
|
-
tasks.cancelDelete()
|
|
240
|
-
})
|
|
241
|
-
})
|
|
242
|
-
})
|
|
839
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
840
|
+
// 6. CHILD ENTITY - Child entity management (not applicable for tasks)
|
|
841
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
842
|
+
// NOTE: Tasks entity does not have child entities.
|
|
843
|
+
// If it did, tests would follow this pattern:
|
|
844
|
+
//
|
|
845
|
+
// describe('SEL_TASK_CHILD_001: Child Entity Selectors', { tags: '@SEL_TASK_CHILD_001' }, () => {
|
|
846
|
+
// /**
|
|
847
|
+
// * Selector: entities.childEntity.container
|
|
848
|
+
// * POM: tasks.selectors.childEntityContainer(childName)
|
|
849
|
+
// * data-cy: tasks-{childName}-section
|
|
850
|
+
// */
|
|
851
|
+
// it('should find child entity container', () => {
|
|
852
|
+
// cy.get(tasks.selectors.childEntityContainer('subtasks')).should('exist')
|
|
853
|
+
// })
|
|
854
|
+
//
|
|
855
|
+
// /**
|
|
856
|
+
// * Selector: entities.childEntity.addButton
|
|
857
|
+
// * POM: tasks.selectors.childEntityAddButton(childName)
|
|
858
|
+
// * data-cy: tasks-{childName}-add
|
|
859
|
+
// */
|
|
860
|
+
// it('should find child entity add button', () => {
|
|
861
|
+
// cy.get(tasks.selectors.childEntityAddButton('subtasks')).should('exist')
|
|
862
|
+
// })
|
|
863
|
+
// })
|
|
243
864
|
})
|