@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
|
@@ -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
|
|
67
|
+
it.skip('should find mobile topbar container (requires mobile viewport)', () => {
|
|
68
68
|
// Requires: cy.viewport('iphone-x')
|
|
69
|
-
cy.get(dashboard.selectors.
|
|
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.
|
|
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
|
|
104
|
-
cy.get(dashboard.selectors.
|
|
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
|
|
112
|
-
cy.get(dashboard.selectors.
|
|
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.
|
|
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
|
|
129
|
-
cy.get(dashboard.selectors.
|
|
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.
|
|
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
|
|
43
|
-
cy.get(dashboard.selectors.
|
|
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', () => {
|