@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.
Files changed (48) hide show
  1. package/components/ai-chat/ChatPanel.tsx +7 -7
  2. package/components/ai-chat/Message.tsx +2 -2
  3. package/components/ai-chat/MessageInput.tsx +3 -3
  4. package/components/ai-chat/MessageList.tsx +3 -3
  5. package/components/ai-chat/TypingIndicator.tsx +2 -2
  6. package/entities/customers/api/docs.md +107 -0
  7. package/entities/customers/api/presets.ts +80 -0
  8. package/entities/pages/api/docs.md +114 -0
  9. package/entities/pages/api/presets.ts +72 -0
  10. package/entities/posts/api/docs.md +120 -0
  11. package/entities/posts/api/presets.ts +74 -0
  12. package/entities/tasks/api/docs.md +126 -0
  13. package/entities/tasks/api/presets.ts +84 -0
  14. package/lib/selectors.ts +2 -2
  15. package/messages/de/admin.json +45 -0
  16. package/messages/en/admin.json +45 -0
  17. package/messages/es/admin.json +45 -0
  18. package/messages/fr/admin.json +45 -0
  19. package/messages/it/admin.json +45 -0
  20. package/messages/pt/admin.json +45 -0
  21. package/package.json +3 -3
  22. package/styles/globals.css +24 -0
  23. package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +491 -0
  24. package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +475 -0
  25. package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
  26. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +14 -14
  27. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +3 -3
  28. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +38 -73
  29. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +21 -42
  30. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +117 -38
  31. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +35 -12
  32. package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +50 -59
  33. package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +15 -23
  34. package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +395 -155
  35. package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +795 -174
  36. package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
  37. package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
  38. package/tests/cypress/src/core/BlockEditorBasePOM.ts +269 -99
  39. package/tests/cypress/src/core/DashboardEntityPOM.ts +1 -1
  40. package/tests/cypress/src/features/DashboardPOM.ts +49 -28
  41. package/tests/cypress/src/features/PageBuilderPOM.ts +20 -0
  42. package/tests/cypress/src/features/SettingsPOM.ts +511 -166
  43. package/tests/cypress/src/features/SuperadminPOM.ts +679 -159
  44. package/tests/cypress/src/features/index.ts +10 -10
  45. package/tests/cypress/e2e/_utils/selectors/pages-editor.bdd.md +0 -207
  46. package/tests/cypress/e2e/_utils/selectors/pages-editor.cy.ts +0 -211
  47. package/tests/cypress/e2e/_utils/selectors/posts-editor.bdd.md +0 -184
  48. package/tests/cypress/e2e/_utils/selectors/posts-editor.cy.ts +0 -350
@@ -0,0 +1,475 @@
1
+ /**
2
+ * Block Editor Selectors Validation Test
3
+ *
4
+ * Comprehensive test suite validating ALL selectors in BlockEditorBasePOM.
5
+ * Uses the `pages` entity as the test base.
6
+ *
7
+ * Test Structure:
8
+ * - SEL_BE_001: Header Selectors
9
+ * - SEL_BE_002: Block Picker Selectors
10
+ * - SEL_BE_003: Entity Fields Panel Selectors
11
+ * - SEL_BE_004: Layout Canvas Selectors
12
+ * - SEL_BE_005: Preview Canvas Selectors
13
+ * - SEL_BE_006: Block Properties Panel Selectors
14
+ * - SEL_BE_007: Array Fields Selectors
15
+ *
16
+ * Re-execution:
17
+ * pnpm tags @SEL_BE_001 # Run only Header tests
18
+ * pnpm tags @SEL_BE_004 # Run only Layout Canvas tests
19
+ * pnpm tags @ui-selectors # Run all selector tests
20
+ */
21
+
22
+ import { PageBuilderPOM } from '../../../src/features/PageBuilderPOM'
23
+ import { PagesPOM } from '../../../src/entities/PagesPOM'
24
+ import { loginAsDefaultDeveloper } from '../../../src/session-helpers'
25
+
26
+ const DEVELOPER_TEAM_ID = 'team-nextspark-001'
27
+
28
+ describe('Block Editor Selectors Validation', {
29
+ tags: ['@ui-selectors', '@block-editor', '@selector-validation']
30
+ }, () => {
31
+ const pom = PageBuilderPOM.create()
32
+ const pages = PagesPOM.create()
33
+
34
+ beforeEach(() => {
35
+ loginAsDefaultDeveloper()
36
+ cy.window().then((win) => {
37
+ win.localStorage.setItem('activeTeamId', DEVELOPER_TEAM_ID)
38
+ })
39
+ })
40
+
41
+ // ===========================================================================
42
+ // SEL_BE_001: HEADER SELECTORS
43
+ // ===========================================================================
44
+ describe('SEL_BE_001: Header Selectors', { tags: '@SEL_BE_001' }, () => {
45
+ beforeEach(() => {
46
+ pom.visitCreate()
47
+ pom.waitForEditor()
48
+ })
49
+
50
+ it('SEL_BE_001_01: should find header container', { tags: '@SEL_BE_001_01' }, () => {
51
+ cy.get(pom.editorSelectors.container).should('exist').and('be.visible')
52
+ })
53
+
54
+ it('SEL_BE_001_02: should find back button', { tags: '@SEL_BE_001_02' }, () => {
55
+ cy.get(pom.editorSelectors.backButton).should('exist').and('be.visible')
56
+ })
57
+
58
+ it('SEL_BE_001_03: should find title input', { tags: '@SEL_BE_001_03' }, () => {
59
+ cy.get(pom.editorSelectors.titleInput).should('exist').and('be.visible')
60
+ })
61
+
62
+ it('SEL_BE_001_04: should find slug input', { tags: '@SEL_BE_001_04' }, () => {
63
+ cy.get(pom.editorSelectors.slugInput).should('exist').and('be.visible')
64
+ })
65
+
66
+ it('SEL_BE_001_05: should find view mode toggle', { tags: '@SEL_BE_001_05' }, () => {
67
+ cy.get(pom.editorSelectors.viewModeToggle).should('exist').and('be.visible')
68
+ })
69
+
70
+ it('SEL_BE_001_06: should find layout mode button (viewEditor)', { tags: '@SEL_BE_001_06' }, () => {
71
+ cy.get(pom.editorSelectors.viewEditor).should('exist').and('be.visible')
72
+ })
73
+
74
+ it('SEL_BE_001_07: should find preview mode button (viewPreview)', { tags: '@SEL_BE_001_07' }, () => {
75
+ cy.get(pom.editorSelectors.viewPreview).should('exist').and('be.visible')
76
+ })
77
+
78
+ it('SEL_BE_001_08: should find save button', { tags: '@SEL_BE_001_08' }, () => {
79
+ cy.get(pom.editorSelectors.saveButton).should('exist').and('be.visible')
80
+ })
81
+
82
+ it('SEL_BE_001_09: should find publish button', { tags: '@SEL_BE_001_09' }, () => {
83
+ cy.get(pom.editorSelectors.publishButton).should('exist').and('be.visible')
84
+ })
85
+
86
+ it('SEL_BE_001_10: should find status dot', { tags: '@SEL_BE_001_10' }, () => {
87
+ cy.get(pom.editorSelectors.statusDot).should('exist').and('be.visible')
88
+ })
89
+
90
+ it('SEL_BE_001_11: should find status label', { tags: '@SEL_BE_001_11' }, () => {
91
+ cy.get(pom.editorSelectors.statusLabel).should('exist').and('be.visible')
92
+ })
93
+ })
94
+
95
+ // ===========================================================================
96
+ // SEL_BE_002: BLOCK PICKER SELECTORS
97
+ // ===========================================================================
98
+ describe('SEL_BE_002: Block Picker Selectors', { tags: '@SEL_BE_002' }, () => {
99
+ beforeEach(() => {
100
+ pom.visitCreate()
101
+ pom.waitForEditor()
102
+ })
103
+
104
+ it('SEL_BE_002_01: should find block picker container', { tags: '@SEL_BE_002_01' }, () => {
105
+ cy.get(pom.editorSelectors.blockPicker).should('exist').and('be.visible')
106
+ })
107
+
108
+ it('SEL_BE_002_02: should find blocks tab', { tags: '@SEL_BE_002_02' }, () => {
109
+ cy.get(pom.editorSelectors.tabBlocks).should('exist').and('be.visible')
110
+ })
111
+
112
+ it('SEL_BE_002_03: should find config tab', { tags: '@SEL_BE_002_03' }, () => {
113
+ cy.get(pom.editorSelectors.tabConfig).should('exist').and('be.visible')
114
+ })
115
+
116
+ it('SEL_BE_002_04: should find search input', { tags: '@SEL_BE_002_04' }, () => {
117
+ cy.get(pom.editorSelectors.blockSearch).should('exist').and('be.visible')
118
+ })
119
+
120
+ it('SEL_BE_002_05: should find category chips container', { tags: '@SEL_BE_002_05' }, () => {
121
+ cy.get(pom.editorSelectors.categoryChips).should('exist').and('be.visible')
122
+ })
123
+
124
+ it('SEL_BE_002_06: should find category chip by name', { tags: '@SEL_BE_002_06' }, () => {
125
+ // Test with 'content' or 'hero' category depending on available blocks
126
+ cy.get('[data-cy^="block-picker-category-"]').first().should('exist').and('be.visible')
127
+ })
128
+
129
+ it('SEL_BE_002_07: should find hero block card', { tags: '@SEL_BE_002_07' }, () => {
130
+ cy.get(pom.editorSelectors.blockItem('hero')).scrollIntoView().should('exist').and('be.visible')
131
+ })
132
+
133
+ it('SEL_BE_002_08: should find hero add button', { tags: '@SEL_BE_002_08' }, () => {
134
+ // Hover to show add button
135
+ cy.get(pom.editorSelectors.blockItem('hero')).trigger('mouseenter')
136
+ cy.get(pom.editorSelectors.addBlock('hero')).should('exist')
137
+ })
138
+ })
139
+
140
+ // ===========================================================================
141
+ // SEL_BE_003: ENTITY FIELDS PANEL SELECTORS
142
+ // ===========================================================================
143
+ describe('SEL_BE_003: Entity Fields Panel Selectors', { tags: '@SEL_BE_003' }, () => {
144
+ let testPageId: string
145
+
146
+ before(() => {
147
+ // Create a test page to ensure we have one to edit
148
+ loginAsDefaultDeveloper()
149
+ cy.request({
150
+ method: 'POST',
151
+ url: '/api/v1/pages',
152
+ headers: {
153
+ 'x-team-id': DEVELOPER_TEAM_ID,
154
+ 'Content-Type': 'application/json'
155
+ },
156
+ body: {
157
+ title: `Selector Test Page ${Date.now()}`,
158
+ slug: `selector-test-${Date.now()}`,
159
+ locale: 'en',
160
+ published: false,
161
+ blocks: []
162
+ }
163
+ }).then((response) => {
164
+ testPageId = response.body.data.id
165
+ })
166
+ })
167
+
168
+ beforeEach(() => {
169
+ pom.visitEdit(testPageId)
170
+ pom.waitForEditor()
171
+ // Switch to config tab to see entity fields
172
+ cy.get(pom.editorSelectors.tabConfig).click()
173
+ })
174
+
175
+ after(() => {
176
+ // Cleanup test page
177
+ if (testPageId) {
178
+ cy.request({
179
+ method: 'DELETE',
180
+ url: `/api/v1/pages/${testPageId}`,
181
+ headers: { 'x-team-id': DEVELOPER_TEAM_ID },
182
+ failOnStatusCode: false
183
+ })
184
+ }
185
+ })
186
+
187
+ it('SEL_BE_003_01: should find entity fields panel container', { tags: '@SEL_BE_003_01' }, () => {
188
+ cy.get(pom.editorSelectors.entityFieldsPanel).should('exist').and('be.visible')
189
+ })
190
+
191
+ // Note: These tests depend on the entity having specific fields configured
192
+ it('SEL_BE_003_02: should find entity field by name', { tags: '@SEL_BE_003_02' }, () => {
193
+ // Check if any field exists - pages might have excerpt, featuredImage, etc.
194
+ // Selector pattern is entity-field-{name}
195
+ cy.get('[data-cy^="entity-field-"]').should('exist')
196
+ })
197
+
198
+ it('SEL_BE_003_03: should find category list (if taxonomies enabled)', { tags: '@SEL_BE_003_03' }, () => {
199
+ // This test is conditional - taxonomies may or may not be enabled
200
+ cy.get('body').then(($body) => {
201
+ if ($body.find(pom.editorSelectors.entityCategoryList).length > 0) {
202
+ cy.get(pom.editorSelectors.entityCategoryList).should('exist')
203
+ } else {
204
+ cy.log('Taxonomies not enabled for pages - skipping')
205
+ }
206
+ })
207
+ })
208
+ })
209
+
210
+ // ===========================================================================
211
+ // SEL_BE_004: LAYOUT CANVAS SELECTORS
212
+ // ===========================================================================
213
+ describe('SEL_BE_004: Layout Canvas Selectors', { tags: '@SEL_BE_004' }, () => {
214
+ beforeEach(() => {
215
+ pom.visitCreate()
216
+ pom.waitForEditor()
217
+ pom.switchToLayoutMode()
218
+ })
219
+
220
+ // When no blocks: only empty state exists (no container)
221
+ it('SEL_BE_004_01: should find empty state when no blocks', { tags: '@SEL_BE_004_01' }, () => {
222
+ cy.get(pom.editorSelectors.layoutCanvasEmpty).should('exist').and('be.visible')
223
+ })
224
+
225
+ describe('With block added', () => {
226
+ beforeEach(() => {
227
+ pom.addBlock('hero')
228
+ // Wait for block to be added
229
+ cy.get(pom.editorSelectors.sortableBlockGeneric).should('have.length.at.least', 1)
230
+ })
231
+
232
+ // Container only exists when there ARE blocks
233
+ it('SEL_BE_004_02: should find layout canvas container with blocks', { tags: '@SEL_BE_004_02' }, () => {
234
+ cy.get(pom.editorSelectors.layoutCanvas).should('exist').and('be.visible')
235
+ })
236
+
237
+ it('SEL_BE_004_03: should find sortable blocks (generic)', { tags: '@SEL_BE_004_03' }, () => {
238
+ cy.get(pom.editorSelectors.sortableBlockGeneric).should('exist')
239
+ })
240
+
241
+ it('SEL_BE_004_04: should find specific sortable block by ID', { tags: '@SEL_BE_004_04' }, () => {
242
+ cy.get(pom.editorSelectors.sortableBlockGeneric).first()
243
+ .invoke('attr', 'data-cy')
244
+ .then((dataCy) => {
245
+ const blockId = dataCy?.replace('sortable-block-', '') || ''
246
+ expect(blockId).to.not.be.empty
247
+ cy.get(pom.editorSelectors.sortableBlock(blockId)).should('exist')
248
+ })
249
+ })
250
+
251
+ it('SEL_BE_004_05: should find drag handle', { tags: '@SEL_BE_004_05' }, () => {
252
+ cy.get(pom.editorSelectors.sortableBlockGeneric).first()
253
+ .invoke('attr', 'data-cy')
254
+ .then((dataCy) => {
255
+ const blockId = dataCy?.replace('sortable-block-', '') || ''
256
+ cy.get(pom.editorSelectors.dragHandle(blockId)).should('exist')
257
+ })
258
+ })
259
+
260
+ it('SEL_BE_004_06: should find duplicate button', { tags: '@SEL_BE_004_06' }, () => {
261
+ cy.get(pom.editorSelectors.sortableBlockGeneric).first()
262
+ .invoke('attr', 'data-cy')
263
+ .then((dataCy) => {
264
+ const blockId = dataCy?.replace('sortable-block-', '') || ''
265
+ cy.get(pom.editorSelectors.duplicateBlock(blockId)).should('exist')
266
+ })
267
+ })
268
+
269
+ it('SEL_BE_004_07: should find remove button', { tags: '@SEL_BE_004_07' }, () => {
270
+ cy.get(pom.editorSelectors.sortableBlockGeneric).first()
271
+ .invoke('attr', 'data-cy')
272
+ .then((dataCy) => {
273
+ const blockId = dataCy?.replace('sortable-block-', '') || ''
274
+ cy.get(pom.editorSelectors.removeBlock(blockId)).should('exist')
275
+ })
276
+ })
277
+
278
+ // Note: sortableBlockName selector is defined but not yet implemented in component
279
+ // This test validates the block card contains the block name text
280
+ it('SEL_BE_004_08: should display block name in sortable card', { tags: '@SEL_BE_004_08' }, () => {
281
+ cy.get(pom.editorSelectors.sortableBlockGeneric).first()
282
+ .should('contain.text', 'Hero') // Hero block should show its name
283
+ })
284
+ })
285
+ })
286
+
287
+ // ===========================================================================
288
+ // SEL_BE_005: PREVIEW CANVAS SELECTORS
289
+ // ===========================================================================
290
+ describe('SEL_BE_005: Preview Canvas Selectors', { tags: '@SEL_BE_005' }, () => {
291
+ beforeEach(() => {
292
+ pom.visitCreate()
293
+ pom.waitForEditor()
294
+ // Add a block first, then switch to preview
295
+ pom.switchToLayoutMode()
296
+ pom.addBlock('hero')
297
+ cy.get(pom.editorSelectors.sortableBlockGeneric).should('have.length.at.least', 1)
298
+ pom.switchToPreviewMode()
299
+ })
300
+
301
+ it('SEL_BE_005_01: should find preview canvas container', { tags: '@SEL_BE_005_01' }, () => {
302
+ cy.get(pom.editorSelectors.previewCanvas).should('exist').and('be.visible')
303
+ })
304
+
305
+ it('SEL_BE_005_02: should find preview blocks (generic)', { tags: '@SEL_BE_005_02' }, () => {
306
+ cy.get(pom.editorSelectors.previewBlockGeneric).should('exist')
307
+ })
308
+
309
+ it('SEL_BE_005_03: should find specific preview block by ID', { tags: '@SEL_BE_005_03' }, () => {
310
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
311
+ .invoke('attr', 'data-cy')
312
+ .then((dataCy) => {
313
+ const blockId = dataCy?.replace('preview-block-', '') || ''
314
+ expect(blockId).to.not.be.empty
315
+ cy.get(pom.editorSelectors.previewBlock(blockId)).should('exist')
316
+ })
317
+ })
318
+
319
+ it('SEL_BE_005_04: should find floating toolbar on hover', { tags: '@SEL_BE_005_04' }, () => {
320
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
321
+ .invoke('attr', 'data-cy')
322
+ .then((dataCy) => {
323
+ const blockId = dataCy?.replace('preview-block-', '') || ''
324
+ // Hover to show toolbar
325
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
326
+ cy.get(pom.editorSelectors.floatingToolbar(blockId)).should('exist')
327
+ })
328
+ })
329
+
330
+ it('SEL_BE_005_05: should find toolbar block name', { tags: '@SEL_BE_005_05' }, () => {
331
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
332
+ .invoke('attr', 'data-cy')
333
+ .then((dataCy) => {
334
+ const blockId = dataCy?.replace('preview-block-', '') || ''
335
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
336
+ cy.get(pom.editorSelectors.floatingToolbarName(blockId)).should('exist')
337
+ })
338
+ })
339
+
340
+ it('SEL_BE_005_06: should find toolbar duplicate button', { tags: '@SEL_BE_005_06' }, () => {
341
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
342
+ .invoke('attr', 'data-cy')
343
+ .then((dataCy) => {
344
+ const blockId = dataCy?.replace('preview-block-', '') || ''
345
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
346
+ cy.get(pom.editorSelectors.floatingToolbarDuplicate(blockId)).should('exist')
347
+ })
348
+ })
349
+
350
+ it('SEL_BE_005_07: should find toolbar delete button', { tags: '@SEL_BE_005_07' }, () => {
351
+ cy.get(pom.editorSelectors.previewBlockGeneric).first()
352
+ .invoke('attr', 'data-cy')
353
+ .then((dataCy) => {
354
+ const blockId = dataCy?.replace('preview-block-', '') || ''
355
+ cy.get(pom.editorSelectors.previewBlock(blockId)).trigger('mouseenter')
356
+ cy.get(pom.editorSelectors.floatingToolbarDelete(blockId)).should('exist')
357
+ })
358
+ })
359
+ })
360
+
361
+ // ===========================================================================
362
+ // SEL_BE_006: BLOCK PROPERTIES PANEL SELECTORS
363
+ // ===========================================================================
364
+ describe('SEL_BE_006: Block Properties Panel Selectors', { tags: '@SEL_BE_006' }, () => {
365
+ beforeEach(() => {
366
+ pom.visitCreate()
367
+ pom.waitForEditor()
368
+ })
369
+
370
+ // When no block selected: only empty state exists (no container)
371
+ it('SEL_BE_006_01: should find empty state when no block selected', { tags: '@SEL_BE_006_01' }, () => {
372
+ cy.get(pom.editorSelectors.blockPropertiesEmpty).should('exist').and('be.visible')
373
+ })
374
+
375
+ describe('With block selected', () => {
376
+ beforeEach(() => {
377
+ pom.switchToLayoutMode()
378
+ pom.addBlock('hero')
379
+ // Block is auto-selected after adding
380
+ cy.get(pom.editorSelectors.sortableBlockGeneric).should('have.length.at.least', 1)
381
+ })
382
+
383
+ // Container only exists when a block IS selected
384
+ it('SEL_BE_006_02: should find properties panel container with block selected', { tags: '@SEL_BE_006_02' }, () => {
385
+ cy.get(pom.editorSelectors.blockPropertiesPanel).should('exist').and('be.visible')
386
+ })
387
+
388
+ it('SEL_BE_006_03: should find panel header', { tags: '@SEL_BE_006_03' }, () => {
389
+ cy.get(pom.editorSelectors.blockPropertiesHeader).should('exist').and('be.visible')
390
+ })
391
+
392
+ it('SEL_BE_006_04: should find block name in header', { tags: '@SEL_BE_006_04' }, () => {
393
+ cy.get(pom.editorSelectors.blockPropertiesName).should('exist').and('be.visible')
394
+ })
395
+
396
+ it('SEL_BE_006_05: should find content tab', { tags: '@SEL_BE_006_05' }, () => {
397
+ cy.get(pom.editorSelectors.tabContent).should('exist').and('be.visible')
398
+ })
399
+
400
+ it('SEL_BE_006_06: should find design tab', { tags: '@SEL_BE_006_06' }, () => {
401
+ cy.get(pom.editorSelectors.tabDesign).should('exist').and('be.visible')
402
+ })
403
+
404
+ it('SEL_BE_006_07: should find advanced tab', { tags: '@SEL_BE_006_07' }, () => {
405
+ cy.get(pom.editorSelectors.tabAdvanced).should('exist').and('be.visible')
406
+ })
407
+
408
+ it('SEL_BE_006_08: should find dynamic form container', { tags: '@SEL_BE_006_08' }, () => {
409
+ cy.get(pom.editorSelectors.dynamicForm).should('exist').and('be.visible')
410
+ })
411
+
412
+ it('SEL_BE_006_09: should find dynamic field by name', { tags: '@SEL_BE_006_09' }, () => {
413
+ // Hero block should have a title field
414
+ cy.get(pom.editorSelectors.dynamicField('title')).should('exist')
415
+ })
416
+ })
417
+ })
418
+
419
+ // ===========================================================================
420
+ // SEL_BE_007: ARRAY FIELDS SELECTORS
421
+ // ===========================================================================
422
+ describe('SEL_BE_007: Array Fields Selectors', { tags: '@SEL_BE_007' }, () => {
423
+ beforeEach(() => {
424
+ pom.visitCreate()
425
+ pom.waitForEditor()
426
+ pom.switchToLayoutMode()
427
+ // Add features-grid block which has array fields
428
+ pom.addBlock('features-grid')
429
+ cy.get(pom.editorSelectors.sortableBlockGeneric).should('have.length.at.least', 1)
430
+ })
431
+
432
+ it('SEL_BE_007_01: should find array field container', { tags: '@SEL_BE_007_01' }, () => {
433
+ // features-grid has 'features' array field - selector is block-array-{name}
434
+ cy.get('[data-cy^="block-array-"]').should('exist')
435
+ })
436
+
437
+ it('SEL_BE_007_02: should find array field add button', { tags: '@SEL_BE_007_02' }, () => {
438
+ // Selector is block-array-{name}-add
439
+ cy.get('[data-cy$="-add"][data-cy^="block-array-"]').should('exist')
440
+ })
441
+
442
+ describe('With array items', () => {
443
+ beforeEach(() => {
444
+ // Click add button to add an item
445
+ cy.get('[data-cy$="-add"][data-cy^="block-array-"]').first().click()
446
+ // Wait for item controls to appear - use remove button as indicator
447
+ // Selector pattern is block-array-{name}-{index}-remove
448
+ cy.get('[data-cy$="-remove"][data-cy^="block-array-"]', { timeout: 10000 }).should('have.length.at.least', 1)
449
+ })
450
+
451
+ it('SEL_BE_007_03: should find array field item controls', { tags: '@SEL_BE_007_03' }, () => {
452
+ // Item exists if it has remove button
453
+ cy.get('[data-cy$="-remove"][data-cy^="block-array-"]').first().should('exist')
454
+ })
455
+
456
+ it('SEL_BE_007_04: should find array field item move up button', { tags: '@SEL_BE_007_04' }, () => {
457
+ // Add second item for move buttons to be enabled
458
+ cy.get('[data-cy$="-add"][data-cy^="block-array-"]').first().click()
459
+ cy.get('[data-cy$="-remove"][data-cy^="block-array-"]').should('have.length.at.least', 2)
460
+ // Selector is block-array-{name}-{index}-up
461
+ cy.get('[data-cy$="-up"][data-cy^="block-array-"]').should('exist')
462
+ })
463
+
464
+ it('SEL_BE_007_05: should find array field item move down button', { tags: '@SEL_BE_007_05' }, () => {
465
+ // Selector is block-array-{name}-{index}-down
466
+ cy.get('[data-cy$="-down"][data-cy^="block-array-"]').should('exist')
467
+ })
468
+
469
+ it('SEL_BE_007_06: should find array field item remove button', { tags: '@SEL_BE_007_06' }, () => {
470
+ // Selector is block-array-{name}-{index}-remove
471
+ cy.get('[data-cy$="-remove"][data-cy^="block-array-"]').should('exist')
472
+ })
473
+ })
474
+ })
475
+ })
@@ -0,0 +1,52 @@
1
+ /**
2
+ * UI Selectors Validation: Dashboard Container
3
+ *
4
+ * This test validates that the dashboard container selector exists in the DOM.
5
+ * This is a lightweight test that ONLY checks selector presence, not functionality.
6
+ *
7
+ * Purpose:
8
+ * - Validate DashboardPOM container selector works correctly
9
+ * - Ensure dashboard.container selector is implemented
10
+ * - Run as Phase 12 sub-gate before functional tests
11
+ *
12
+ * Scope:
13
+ * - Navigate to dashboard (requires login)
14
+ * - Assert main container exists in DOM
15
+ * - Fast execution (< 10 seconds)
16
+ *
17
+ * Test IDs:
18
+ * - SEL_DCNT_001: Dashboard Container Structure (1 selector)
19
+ *
20
+ * Component: DashboardShell.tsx
21
+ * Selector: dashboard.container → 'dashboard-container'
22
+ */
23
+
24
+ import { DashboardPOM } from '../../../src/features/DashboardPOM'
25
+ import { loginAsDefaultDeveloper } from '../../../src/session-helpers'
26
+
27
+ describe('Dashboard Container Selectors Validation', { tags: ['@ui-selectors', '@dashboard', '@container'] }, () => {
28
+ const dashboard = DashboardPOM.create()
29
+
30
+ beforeEach(() => {
31
+ loginAsDefaultDeveloper()
32
+ cy.visit('/dashboard', { timeout: 60000, failOnStatusCode: false })
33
+ cy.url().should('include', '/dashboard')
34
+ })
35
+
36
+ // ============================================
37
+ // SEL_DCNT_001: DASHBOARD CONTAINER (1 selector)
38
+ // ============================================
39
+ describe('SEL_DCNT_001: Dashboard Container', { tags: '@SEL_DCNT_001' }, () => {
40
+ it('should find dashboard main container', () => {
41
+ cy.get(dashboard.selectors.container).should('exist')
42
+ })
43
+
44
+ it('should have dashboard container visible', () => {
45
+ cy.get(dashboard.selectors.container).should('be.visible')
46
+ })
47
+
48
+ it('should have correct data-cy attribute value', () => {
49
+ cy.get('[data-cy="dashboard-container"]').should('exist')
50
+ })
51
+ })
52
+ })
@@ -27,7 +27,7 @@
27
27
 
28
28
  import { DashboardPOM } from '../../../src/features/DashboardPOM'
29
29
 
30
- describe('Dashboard Mobile Selectors Validation', { tags: ['@ui-selectors', '@mobile'] }, () => {
30
+ describe('Dashboard Mobile Selectors Validation', { tags: ['@ui-selectors', '@dashboard', '@mobile'] }, () => {
31
31
  const dashboard = DashboardPOM.create()
32
32
 
33
33
  // ============================================
@@ -64,9 +64,9 @@ describe('Dashboard Mobile Selectors Validation', { tags: ['@ui-selectors', '@mo
64
64
  // NOTE: Only visible on mobile viewports
65
65
  // ============================================
66
66
  describe('SEL_DMOB_001: Mobile Topbar Selectors', { tags: '@SEL_DMOB_001' }, () => {
67
- it.skip('should find mobile topbar header (requires mobile viewport)', () => {
67
+ it.skip('should find mobile topbar container (requires mobile viewport)', () => {
68
68
  // Requires: cy.viewport('iphone-x')
69
- cy.get(dashboard.selectors.mobileTopbarHeader).should('exist')
69
+ cy.get(dashboard.selectors.mobileTopbarContainer).should('exist')
70
70
  })
71
71
 
72
72
  it.skip('should find mobile user profile button (requires mobile viewport)', () => {
@@ -86,8 +86,8 @@ describe('Dashboard Mobile Selectors Validation', { tags: ['@ui-selectors', '@mo
86
86
  // SEL_DMOB_002: MOBILE BOTTOM NAV (2 selectors)
87
87
  // ============================================
88
88
  describe('SEL_DMOB_002: Mobile Bottom Nav Selectors', { tags: '@SEL_DMOB_002' }, () => {
89
- it.skip('should find mobile bottom nav (requires mobile viewport)', () => {
90
- cy.get(dashboard.selectors.mobileBottomNav).should('exist')
89
+ it.skip('should find mobile bottom nav container (requires mobile viewport)', () => {
90
+ cy.get(dashboard.selectors.mobileBottomNavContainer).should('exist')
91
91
  })
92
92
 
93
93
  it.skip('should find mobile bottom nav item (requires mobile viewport)', () => {
@@ -100,16 +100,16 @@ describe('Dashboard Mobile Selectors Validation', { tags: ['@ui-selectors', '@mo
100
100
  // SEL_DMOB_003: MOBILE MORE SHEET (5 selectors)
101
101
  // ============================================
102
102
  describe('SEL_DMOB_003: Mobile More Sheet Selectors', { tags: '@SEL_DMOB_003' }, () => {
103
- it.skip('should find mobile more sheet content (requires mobile viewport)', () => {
104
- cy.get(dashboard.selectors.mobileMoreSheetContent).should('exist')
103
+ it.skip('should find mobile more sheet container (requires mobile viewport)', () => {
104
+ cy.get(dashboard.selectors.mobileMoreSheetContainer).should('exist')
105
105
  })
106
106
 
107
107
  it.skip('should find mobile more sheet item (requires mobile viewport)', () => {
108
108
  cy.get(dashboard.selectors.mobileMoreSheetItem('settings')).should('exist')
109
109
  })
110
110
 
111
- it.skip('should find mobile more sheet sector7 link (requires mobile viewport)', () => {
112
- cy.get(dashboard.selectors.mobileMoreSheetSuperadmin).should('exist')
111
+ it.skip('should find mobile more sheet superadmin link (requires mobile viewport)', () => {
112
+ cy.get(dashboard.selectors.mobileMoreSheetSuperadminLink).should('exist')
113
113
  })
114
114
 
115
115
  it.skip('should find mobile more sheet team switcher (requires mobile viewport)', () => {
@@ -117,7 +117,7 @@ describe('Dashboard Mobile Selectors Validation', { tags: ['@ui-selectors', '@mo
117
117
  })
118
118
 
119
119
  it.skip('should find mobile more sheet signout button (requires mobile viewport)', () => {
120
- cy.get(dashboard.selectors.mobileMoreSheetSignout).should('exist')
120
+ cy.get(dashboard.selectors.mobileMoreSheetSignoutButton).should('exist')
121
121
  })
122
122
  })
123
123
 
@@ -125,13 +125,13 @@ describe('Dashboard Mobile Selectors Validation', { tags: ['@ui-selectors', '@mo
125
125
  // SEL_DMOB_004: MOBILE QUICK CREATE SHEET (2 selectors)
126
126
  // ============================================
127
127
  describe('SEL_DMOB_004: Mobile Quick Create Selectors', { tags: '@SEL_DMOB_004' }, () => {
128
- it.skip('should find mobile quick create sheet content (requires mobile viewport)', () => {
129
- cy.get(dashboard.selectors.mobileQuickCreateContent).should('exist')
128
+ it.skip('should find mobile quick create sheet container (requires mobile viewport)', () => {
129
+ cy.get(dashboard.selectors.mobileQuickCreateSheetContainer).should('exist')
130
130
  })
131
131
 
132
- it.skip('should find mobile quick create item (requires mobile viewport)', () => {
132
+ it.skip('should find mobile quick create sheet item (requires mobile viewport)', () => {
133
133
  // Example: tasks, customers, posts, pages
134
- cy.get(dashboard.selectors.mobileQuickCreateItem('tasks')).should('exist')
134
+ cy.get(dashboard.selectors.mobileQuickCreateSheetItem('tasks')).should('exist')
135
135
  })
136
136
  })
137
137
  })
@@ -26,7 +26,7 @@
26
26
  import { DashboardPOM } from '../../../src/features/DashboardPOM'
27
27
  import { loginAsDefaultDeveloper } from '../../../src/session-helpers'
28
28
 
29
- describe('Dashboard Navigation Selectors Validation', { tags: ['@ui-selectors', '@dashboard'] }, () => {
29
+ describe('Dashboard Navigation Selectors Validation', { tags: ['@ui-selectors', '@dashboard', '@navigation'] }, () => {
30
30
  const dashboard = DashboardPOM.create()
31
31
 
32
32
  beforeEach(() => {
@@ -39,8 +39,8 @@ describe('Dashboard Navigation Selectors Validation', { tags: ['@ui-selectors',
39
39
  // SEL_DASH_001: NAVIGATION STRUCTURE
40
40
  // ============================================
41
41
  describe('SEL_DASH_001: Navigation Structure', { tags: '@SEL_DASH_001' }, () => {
42
- it('should find nav main container', () => {
43
- cy.get(dashboard.selectors.navMain).should('exist')
42
+ it('should find nav container', () => {
43
+ cy.get(dashboard.selectors.navContainer).should('exist')
44
44
  })
45
45
 
46
46
  it('should find dashboard link', () => {