@nextsparkjs/theme-blog 0.1.0-beta.18 → 0.1.0-beta.20
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/package.json +2 -2
- package/tests/cypress/e2e/README.md +170 -0
- package/tests/cypress/e2e/categories/categories-crud.cy.ts +322 -0
- package/tests/cypress/e2e/categories/categories-crud.md +73 -0
- package/tests/cypress/e2e/posts/posts-crud.cy.ts +460 -0
- package/tests/cypress/e2e/posts/posts-crud.md +115 -0
- package/tests/cypress/e2e/posts/posts-editor.cy.ts +290 -0
- package/tests/cypress/e2e/posts/posts-editor.md +139 -0
- package/tests/cypress/e2e/posts/posts-status-workflow.cy.ts +302 -0
- package/tests/cypress/e2e/posts/posts-status-workflow.md +83 -0
- package/tests/cypress/fixtures/blocks.json +9 -0
- package/tests/cypress/fixtures/entities.json +42 -0
- package/tests/cypress/src/FeaturedImageUpload.js +131 -0
- package/tests/cypress/src/PostEditor.js +386 -0
- package/tests/cypress/src/PostsList.js +350 -0
- package/tests/cypress/src/WysiwygEditor.js +373 -0
- package/tests/cypress/src/components/EntityForm.ts +357 -0
- package/tests/cypress/src/components/EntityList.ts +360 -0
- package/tests/cypress/src/components/PostEditorPOM.ts +447 -0
- package/tests/cypress/src/components/PostsPOM.ts +362 -0
- package/tests/cypress/src/components/index.ts +18 -0
- package/tests/cypress/src/index.js +33 -0
- package/tests/cypress/src/selectors.ts +49 -0
- package/tests/cypress/src/session-helpers.ts +89 -0
- package/tests/cypress/support/e2e.ts +89 -0
- package/tests/cypress.config.ts +165 -0
- package/tests/tsconfig.json +15 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Posts Status Workflow Tests - Blog Theme
|
|
5
|
+
*
|
|
6
|
+
* Tests for publish/unpublish workflow and auto-save functionality.
|
|
7
|
+
*
|
|
8
|
+
* Theme Mode: single-user (isolated blogs, no team collaboration)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { PostsList } from '../../../src/PostsList.js'
|
|
12
|
+
import { PostEditor } from '../../../src/PostEditor.js'
|
|
13
|
+
import { loginAsBlogAuthor } from '../../../src/session-helpers'
|
|
14
|
+
|
|
15
|
+
describe('Posts Status Workflow - Publish/Unpublish', () => {
|
|
16
|
+
const postsList = new PostsList()
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
loginAsBlogAuthor('MARCOS')
|
|
20
|
+
cy.visit('/dashboard/posts')
|
|
21
|
+
postsList.validateListVisible()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('PUBLISH - Draft to Published', () => {
|
|
25
|
+
it('BLOG_STATUS_001: should publish draft from list actions', () => {
|
|
26
|
+
// Create a draft post first
|
|
27
|
+
const postTitle = `Publish From List ${Date.now()}`
|
|
28
|
+
const postEditor = new PostEditor('create')
|
|
29
|
+
|
|
30
|
+
postsList.clickCreate()
|
|
31
|
+
postEditor.fillTitle(postTitle)
|
|
32
|
+
postEditor.typeContent('Draft post to be published from list')
|
|
33
|
+
postEditor.saveDraft()
|
|
34
|
+
postEditor.clickBack()
|
|
35
|
+
|
|
36
|
+
// Wait for post to appear
|
|
37
|
+
cy.contains(postTitle).should('be.visible')
|
|
38
|
+
|
|
39
|
+
// Find the post and publish via actions menu
|
|
40
|
+
cy.contains('[data-cy^="posts-row-"]', postTitle)
|
|
41
|
+
.invoke('attr', 'data-cy')
|
|
42
|
+
.then(dataCy => {
|
|
43
|
+
const postId = dataCy?.replace('posts-row-', '')
|
|
44
|
+
if (postId) {
|
|
45
|
+
postsList.clickPublish(postId)
|
|
46
|
+
|
|
47
|
+
// Wait for status update
|
|
48
|
+
cy.wait(500)
|
|
49
|
+
|
|
50
|
+
// Validate status changed
|
|
51
|
+
postsList.validatePostStatus(postId, 'published')
|
|
52
|
+
|
|
53
|
+
cy.log('✅ Published draft from list successfully')
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('BLOG_STATUS_002: should publish draft from editor', () => {
|
|
59
|
+
// Create a draft post
|
|
60
|
+
const postTitle = `Publish From Editor ${Date.now()}`
|
|
61
|
+
const postEditor = new PostEditor('create')
|
|
62
|
+
|
|
63
|
+
postsList.clickCreate()
|
|
64
|
+
postEditor.fillTitle(postTitle)
|
|
65
|
+
postEditor.typeContent('Draft post to be published from editor')
|
|
66
|
+
postEditor.saveDraft()
|
|
67
|
+
|
|
68
|
+
// Now on edit page, publish
|
|
69
|
+
const editEditor = new PostEditor('edit')
|
|
70
|
+
editEditor.validatePageVisible()
|
|
71
|
+
|
|
72
|
+
// Intercept the PATCH request to verify status is sent correctly
|
|
73
|
+
cy.intercept('PATCH', '/api/v1/posts/*').as('updatePost')
|
|
74
|
+
|
|
75
|
+
// Publish
|
|
76
|
+
editEditor.publish()
|
|
77
|
+
|
|
78
|
+
// Wait for the API call to complete
|
|
79
|
+
cy.wait('@updatePost').then((interception) => {
|
|
80
|
+
expect(interception.request.body.status).to.equal('published')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// Wait for UI to update
|
|
84
|
+
cy.wait(500)
|
|
85
|
+
|
|
86
|
+
// Validate unpublish button appears (meaning status is now published)
|
|
87
|
+
cy.get('[data-cy="post-edit-unpublish"]', { timeout: 5000 }).should('be.visible')
|
|
88
|
+
|
|
89
|
+
cy.log('✅ Published draft from editor successfully')
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('UNPUBLISH - Published to Draft', () => {
|
|
94
|
+
it('BLOG_STATUS_003: should unpublish post from list actions', () => {
|
|
95
|
+
// Create and publish a post first
|
|
96
|
+
const postTitle = `Unpublish From List ${Date.now()}`
|
|
97
|
+
const postEditor = new PostEditor('create')
|
|
98
|
+
|
|
99
|
+
// Intercept the POST request to confirm publish works
|
|
100
|
+
cy.intercept('POST', '/api/v1/posts').as('createPost')
|
|
101
|
+
|
|
102
|
+
postsList.clickCreate()
|
|
103
|
+
postEditor.fillTitle(postTitle)
|
|
104
|
+
postEditor.typeContent('Published post to be unpublished')
|
|
105
|
+
postEditor.publish()
|
|
106
|
+
|
|
107
|
+
// Wait for create with publish status
|
|
108
|
+
cy.wait('@createPost').then((interception) => {
|
|
109
|
+
expect(interception.request.body.status).to.equal('published')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// After publish, we're on edit page - verify unpublish button exists
|
|
113
|
+
cy.get('[data-cy="post-edit-unpublish"]', { timeout: 5000 }).should('be.visible')
|
|
114
|
+
|
|
115
|
+
// Go back to list
|
|
116
|
+
const editEditor = new PostEditor('edit')
|
|
117
|
+
editEditor.clickBack()
|
|
118
|
+
|
|
119
|
+
// Wait for post to appear
|
|
120
|
+
cy.contains(postTitle).should('be.visible')
|
|
121
|
+
|
|
122
|
+
// Find the post and unpublish via actions menu
|
|
123
|
+
cy.contains('[data-cy^="posts-row-"]', postTitle)
|
|
124
|
+
.invoke('attr', 'data-cy')
|
|
125
|
+
.then(dataCy => {
|
|
126
|
+
const postId = dataCy?.replace('posts-row-', '')
|
|
127
|
+
if (postId) {
|
|
128
|
+
// Intercept the unpublish PATCH
|
|
129
|
+
cy.intercept('PATCH', `/api/v1/posts/${postId}`).as('unpublishPost')
|
|
130
|
+
|
|
131
|
+
postsList.clickUnpublish(postId)
|
|
132
|
+
|
|
133
|
+
// Wait for unpublish API call
|
|
134
|
+
cy.wait('@unpublishPost')
|
|
135
|
+
|
|
136
|
+
// Wait for status update
|
|
137
|
+
cy.wait(500)
|
|
138
|
+
|
|
139
|
+
// Validate status changed
|
|
140
|
+
postsList.validatePostStatus(postId, 'draft')
|
|
141
|
+
|
|
142
|
+
cy.log('✅ Unpublished post from list successfully')
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('BLOG_STATUS_004: should unpublish post from editor', () => {
|
|
148
|
+
// Create and publish a post
|
|
149
|
+
const postTitle = `Unpublish From Editor ${Date.now()}`
|
|
150
|
+
const postEditor = new PostEditor('create')
|
|
151
|
+
|
|
152
|
+
// Intercept the POST request to verify publish status
|
|
153
|
+
cy.intercept('POST', '/api/v1/posts').as('createPost')
|
|
154
|
+
|
|
155
|
+
postsList.clickCreate()
|
|
156
|
+
postEditor.fillTitle(postTitle)
|
|
157
|
+
postEditor.typeContent('Published post to be unpublished from editor')
|
|
158
|
+
postEditor.publish()
|
|
159
|
+
|
|
160
|
+
// Wait for create with publish status
|
|
161
|
+
cy.wait('@createPost').then((interception) => {
|
|
162
|
+
expect(interception.request.body.status).to.equal('published')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
// After publish redirect, verify unpublish button is visible
|
|
166
|
+
cy.get('[data-cy="post-edit-unpublish"]', { timeout: 5000 }).should('be.visible')
|
|
167
|
+
|
|
168
|
+
// Now on edit page with published status
|
|
169
|
+
const editEditor = new PostEditor('edit')
|
|
170
|
+
|
|
171
|
+
// Intercept the PATCH request for unpublish
|
|
172
|
+
cy.intercept('PATCH', '/api/v1/posts/*').as('unpublishPost')
|
|
173
|
+
|
|
174
|
+
// Unpublish
|
|
175
|
+
editEditor.unpublish()
|
|
176
|
+
|
|
177
|
+
// Wait for unpublish API call
|
|
178
|
+
cy.wait('@unpublishPost').then((interception) => {
|
|
179
|
+
expect(interception.request.body.status).to.equal('draft')
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// Wait for UI to update
|
|
183
|
+
cy.wait(500)
|
|
184
|
+
|
|
185
|
+
// Validate publish button is back (meaning post is draft)
|
|
186
|
+
cy.get('[data-cy="post-edit-publish"]', { timeout: 5000 }).should('be.visible')
|
|
187
|
+
|
|
188
|
+
cy.log('✅ Unpublished post from editor successfully')
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe('AUTO-SAVE - Draft auto-save functionality', () => {
|
|
193
|
+
it('BLOG_STATUS_005: should auto-save draft after changes', () => {
|
|
194
|
+
// Create a draft post
|
|
195
|
+
const postTitle = `Auto Save Test ${Date.now()}`
|
|
196
|
+
const postEditor = new PostEditor('create')
|
|
197
|
+
|
|
198
|
+
postsList.clickCreate()
|
|
199
|
+
postEditor.fillTitle(postTitle)
|
|
200
|
+
postEditor.typeContent('Initial content for auto-save test')
|
|
201
|
+
postEditor.saveDraft()
|
|
202
|
+
|
|
203
|
+
// Now on edit page
|
|
204
|
+
const editEditor = new PostEditor('edit')
|
|
205
|
+
editEditor.validatePageVisible()
|
|
206
|
+
|
|
207
|
+
// Make a change
|
|
208
|
+
editEditor.fillExcerpt('Updated excerpt')
|
|
209
|
+
|
|
210
|
+
// Wait for auto-save (30 seconds is too long for tests)
|
|
211
|
+
// Instead, manually save and check the indicator
|
|
212
|
+
editEditor.saveDraft()
|
|
213
|
+
editEditor.validateAutoSaved()
|
|
214
|
+
|
|
215
|
+
cy.log('✅ Auto-save indicator works correctly')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('BLOG_STATUS_006: should show unsaved changes indicator', () => {
|
|
219
|
+
// Create a draft post
|
|
220
|
+
const postTitle = `Unsaved Changes ${Date.now()}`
|
|
221
|
+
const postEditor = new PostEditor('create')
|
|
222
|
+
|
|
223
|
+
postsList.clickCreate()
|
|
224
|
+
postEditor.fillTitle(postTitle)
|
|
225
|
+
postEditor.typeContent('Content for unsaved changes test')
|
|
226
|
+
postEditor.saveDraft()
|
|
227
|
+
|
|
228
|
+
// Now on edit page
|
|
229
|
+
const editEditor = new PostEditor('edit')
|
|
230
|
+
editEditor.validatePageVisible()
|
|
231
|
+
|
|
232
|
+
// Make a change
|
|
233
|
+
editEditor.fillTitle(`${postTitle} - Modified`)
|
|
234
|
+
|
|
235
|
+
// The status should show unsaved changes indicator
|
|
236
|
+
cy.get('[data-cy="post-unsaved-indicator"]').should('be.visible')
|
|
237
|
+
|
|
238
|
+
cy.log('✅ Unsaved changes indicator shown correctly')
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('FEATURED - Featured posts management', () => {
|
|
243
|
+
it('BLOG_STATUS_007: should mark post as featured', () => {
|
|
244
|
+
// Create a post
|
|
245
|
+
const postTitle = `Featured Post ${Date.now()}`
|
|
246
|
+
const postEditor = new PostEditor('create')
|
|
247
|
+
|
|
248
|
+
postsList.clickCreate()
|
|
249
|
+
postEditor.fillTitle(postTitle)
|
|
250
|
+
postEditor.typeContent('This post will be featured')
|
|
251
|
+
postEditor.toggleFeatured(true)
|
|
252
|
+
postEditor.publish()
|
|
253
|
+
|
|
254
|
+
// Validate featured state
|
|
255
|
+
const editEditor = new PostEditor('edit')
|
|
256
|
+
editEditor.validateFeaturedState(true)
|
|
257
|
+
|
|
258
|
+
// Go back to list
|
|
259
|
+
editEditor.clickBack()
|
|
260
|
+
|
|
261
|
+
// Validate we're back on posts list
|
|
262
|
+
postsList.validateListVisible()
|
|
263
|
+
|
|
264
|
+
cy.log('✅ Post marked as featured successfully')
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('BLOG_STATUS_008: should remove featured status', () => {
|
|
268
|
+
// Create a featured post
|
|
269
|
+
const postTitle = `Remove Featured ${Date.now()}`
|
|
270
|
+
const postEditor = new PostEditor('create')
|
|
271
|
+
|
|
272
|
+
postsList.clickCreate()
|
|
273
|
+
postEditor.fillTitle(postTitle)
|
|
274
|
+
postEditor.typeContent('This post will have featured removed')
|
|
275
|
+
|
|
276
|
+
// Enable featured
|
|
277
|
+
cy.get('[data-cy="post-create-featured-toggle"]').click({ force: true })
|
|
278
|
+
cy.wait(300)
|
|
279
|
+
|
|
280
|
+
postEditor.saveDraft()
|
|
281
|
+
|
|
282
|
+
// Wait for save
|
|
283
|
+
cy.wait(1000)
|
|
284
|
+
|
|
285
|
+
// Now on edit page - click featured toggle to disable
|
|
286
|
+
cy.get('[data-cy="post-edit-featured-toggle"]').click({ force: true })
|
|
287
|
+
cy.wait(300)
|
|
288
|
+
|
|
289
|
+
// Save changes
|
|
290
|
+
const editEditor = new PostEditor('edit')
|
|
291
|
+
editEditor.saveDraft()
|
|
292
|
+
|
|
293
|
+
cy.wait(500)
|
|
294
|
+
|
|
295
|
+
cy.log('✅ Featured status removed successfully')
|
|
296
|
+
})
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
after(() => {
|
|
300
|
+
cy.log('✅ Posts Status Workflow tests completed')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Posts Status Workflow - E2E Tests
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Tests for publish/unpublish workflow and auto-save functionality in the Blog theme.
|
|
6
|
+
|
|
7
|
+
**Test File:** `test/cypress/e2e/themes/blog/posts/posts-status-workflow.cy.ts`
|
|
8
|
+
|
|
9
|
+
## Workflow Description
|
|
10
|
+
|
|
11
|
+
Posts have two statuses:
|
|
12
|
+
- **Draft** - Not visible to public
|
|
13
|
+
- **Published** - Visible on public blog
|
|
14
|
+
|
|
15
|
+
The workflow supports:
|
|
16
|
+
- Publishing from list (quick action)
|
|
17
|
+
- Publishing from editor (full control)
|
|
18
|
+
- Unpublishing (reverting to draft)
|
|
19
|
+
- Auto-save for drafts
|
|
20
|
+
- Featured post management
|
|
21
|
+
|
|
22
|
+
## Test Coverage
|
|
23
|
+
|
|
24
|
+
### 1. PUBLISH - Draft to Published (2 tests)
|
|
25
|
+
|
|
26
|
+
| ID | Test Case | Description | Status |
|
|
27
|
+
|----|-----------|-------------|--------|
|
|
28
|
+
| BLOG_STATUS_001 | Publish from list | Publish draft via list actions menu | ✅ Passing |
|
|
29
|
+
| BLOG_STATUS_002 | Publish from editor | Publish draft from edit page | ✅ Passing |
|
|
30
|
+
|
|
31
|
+
### 2. UNPUBLISH - Published to Draft (2 tests)
|
|
32
|
+
|
|
33
|
+
| ID | Test Case | Description | Status |
|
|
34
|
+
|----|-----------|-------------|--------|
|
|
35
|
+
| BLOG_STATUS_003 | Unpublish from list | Unpublish via list actions menu | ✅ Passing |
|
|
36
|
+
| BLOG_STATUS_004 | Unpublish from editor | Unpublish from edit page | ✅ Passing |
|
|
37
|
+
|
|
38
|
+
### 3. AUTO-SAVE (2 tests)
|
|
39
|
+
|
|
40
|
+
| ID | Test Case | Description | Status |
|
|
41
|
+
|----|-----------|-------------|--------|
|
|
42
|
+
| BLOG_STATUS_005 | Auto-save indicator | Validate auto-save works | ✅ Passing |
|
|
43
|
+
| BLOG_STATUS_006 | Unsaved changes | Show unsaved changes indicator | ✅ Passing |
|
|
44
|
+
|
|
45
|
+
### 4. FEATURED Posts (2 tests)
|
|
46
|
+
|
|
47
|
+
| ID | Test Case | Description | Status |
|
|
48
|
+
|----|-----------|-------------|--------|
|
|
49
|
+
| BLOG_STATUS_007 | Mark as featured | Toggle featured on | ✅ Passing |
|
|
50
|
+
| BLOG_STATUS_008 | Remove featured | Toggle featured off | ✅ Passing |
|
|
51
|
+
|
|
52
|
+
## Summary
|
|
53
|
+
|
|
54
|
+
| Category | Total | Passing | Pending | Failing |
|
|
55
|
+
|----------|-------|---------|---------|---------|
|
|
56
|
+
| PUBLISH | 2 | 2 | 0 | 0 |
|
|
57
|
+
| UNPUBLISH | 2 | 2 | 0 | 0 |
|
|
58
|
+
| AUTO-SAVE | 2 | 2 | 0 | 0 |
|
|
59
|
+
| FEATURED | 2 | 2 | 0 | 0 |
|
|
60
|
+
| **Total** | **8** | **8** | **0** | **0** |
|
|
61
|
+
|
|
62
|
+
## Data-cy Selectors Used
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
[data-cy="post-edit-status"]
|
|
66
|
+
[data-cy="post-edit-autosaved"]
|
|
67
|
+
[data-cy="post-edit-publish"]
|
|
68
|
+
[data-cy="post-edit-unpublish"]
|
|
69
|
+
[data-cy="post-edit-featured-toggle"]
|
|
70
|
+
[data-cy="posts-publish-{id}"]
|
|
71
|
+
[data-cy="posts-unpublish-{id}"]
|
|
72
|
+
[data-cy="posts-status-{id}"]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Running Tests
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx cypress run --spec "test/cypress/e2e/themes/blog/posts/posts-status-workflow.cy.ts"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
**Last Updated:** 2025-12-04
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./entities.schema.json",
|
|
3
|
+
"_warning": "AUTO-GENERATED by build-registry.mjs - DO NOT EDIT MANUALLY",
|
|
4
|
+
"_theme": "blog",
|
|
5
|
+
"entities": {
|
|
6
|
+
"categories": {
|
|
7
|
+
"slug": "categories",
|
|
8
|
+
"singular": "category",
|
|
9
|
+
"plural": "Categories",
|
|
10
|
+
"tableName": "categories",
|
|
11
|
+
"fields": [
|
|
12
|
+
"name",
|
|
13
|
+
"slug",
|
|
14
|
+
"description",
|
|
15
|
+
"createdAt",
|
|
16
|
+
"updatedAt"
|
|
17
|
+
],
|
|
18
|
+
"filters": [],
|
|
19
|
+
"source": "theme"
|
|
20
|
+
},
|
|
21
|
+
"posts": {
|
|
22
|
+
"slug": "posts",
|
|
23
|
+
"singular": "post",
|
|
24
|
+
"plural": "Posts",
|
|
25
|
+
"tableName": "posts",
|
|
26
|
+
"fields": [
|
|
27
|
+
"title",
|
|
28
|
+
"slug",
|
|
29
|
+
"excerpt",
|
|
30
|
+
"content",
|
|
31
|
+
"featuredImage",
|
|
32
|
+
"featured",
|
|
33
|
+
"status",
|
|
34
|
+
"publishedAt",
|
|
35
|
+
"createdAt",
|
|
36
|
+
"updatedAt"
|
|
37
|
+
],
|
|
38
|
+
"filters": [],
|
|
39
|
+
"source": "theme"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FeaturedImageUpload - Blog Theme Image Upload Component POM
|
|
3
|
+
*
|
|
4
|
+
* Handles drag & drop and file selection for featured images.
|
|
5
|
+
* Supports preview, removal, and error states.
|
|
6
|
+
*/
|
|
7
|
+
export class FeaturedImageUpload {
|
|
8
|
+
static selectors = {
|
|
9
|
+
// Container
|
|
10
|
+
container: '[data-cy="featured-image-container"]',
|
|
11
|
+
|
|
12
|
+
// Upload Area
|
|
13
|
+
dropzone: '[data-cy="featured-image-dropzone"]',
|
|
14
|
+
input: '[data-cy="featured-image-input"]',
|
|
15
|
+
|
|
16
|
+
// Preview
|
|
17
|
+
preview: '[data-cy="featured-image-preview"]',
|
|
18
|
+
remove: '[data-cy="featured-image-remove"]',
|
|
19
|
+
|
|
20
|
+
// States
|
|
21
|
+
loading: '[data-cy="featured-image-loading"]',
|
|
22
|
+
error: '[data-cy="featured-image-error"]',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Validate component is visible
|
|
27
|
+
*/
|
|
28
|
+
validateVisible() {
|
|
29
|
+
cy.get(FeaturedImageUpload.selectors.container).should('be.visible')
|
|
30
|
+
return this
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Validate dropzone is visible (no image uploaded)
|
|
35
|
+
*/
|
|
36
|
+
validateDropzoneVisible() {
|
|
37
|
+
cy.get(FeaturedImageUpload.selectors.dropzone).should('be.visible')
|
|
38
|
+
return this
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validate preview is visible (image uploaded)
|
|
43
|
+
*/
|
|
44
|
+
validatePreviewVisible() {
|
|
45
|
+
cy.get(FeaturedImageUpload.selectors.preview).should('be.visible')
|
|
46
|
+
return this
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Upload a file via file input
|
|
51
|
+
* @param {string} filePath - Path to the file to upload
|
|
52
|
+
*/
|
|
53
|
+
uploadFile(filePath) {
|
|
54
|
+
cy.get(FeaturedImageUpload.selectors.input).selectFile(filePath, { force: true })
|
|
55
|
+
return this
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Upload a file via drag and drop
|
|
60
|
+
* @param {string} filePath - Path to the file to upload
|
|
61
|
+
*/
|
|
62
|
+
dragAndDropFile(filePath) {
|
|
63
|
+
cy.get(FeaturedImageUpload.selectors.dropzone).selectFile(filePath, {
|
|
64
|
+
action: 'drag-drop',
|
|
65
|
+
})
|
|
66
|
+
return this
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Click dropzone to trigger file dialog
|
|
71
|
+
*/
|
|
72
|
+
clickDropzone() {
|
|
73
|
+
cy.get(FeaturedImageUpload.selectors.dropzone).click()
|
|
74
|
+
return this
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Remove uploaded image
|
|
79
|
+
*/
|
|
80
|
+
removeImage() {
|
|
81
|
+
cy.get(FeaturedImageUpload.selectors.remove).click()
|
|
82
|
+
return this
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate loading state
|
|
87
|
+
*/
|
|
88
|
+
validateLoading() {
|
|
89
|
+
cy.get(FeaturedImageUpload.selectors.loading).should('be.visible')
|
|
90
|
+
return this
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validate loading is complete
|
|
95
|
+
*/
|
|
96
|
+
validateLoadingComplete() {
|
|
97
|
+
cy.get(FeaturedImageUpload.selectors.loading).should('not.exist')
|
|
98
|
+
return this
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Validate error is shown
|
|
103
|
+
* @param {string} message - Expected error message (optional)
|
|
104
|
+
*/
|
|
105
|
+
validateError(message) {
|
|
106
|
+
cy.get(FeaturedImageUpload.selectors.error).should('be.visible')
|
|
107
|
+
if (message) {
|
|
108
|
+
cy.get(FeaturedImageUpload.selectors.error).should('contain.text', message)
|
|
109
|
+
}
|
|
110
|
+
return this
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validate no error is shown
|
|
115
|
+
*/
|
|
116
|
+
validateNoError() {
|
|
117
|
+
cy.get(FeaturedImageUpload.selectors.error).should('not.exist')
|
|
118
|
+
return this
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Wait for image to be uploaded and preview shown
|
|
123
|
+
*/
|
|
124
|
+
waitForUpload() {
|
|
125
|
+
cy.get(FeaturedImageUpload.selectors.loading).should('not.exist')
|
|
126
|
+
cy.get(FeaturedImageUpload.selectors.preview).should('be.visible')
|
|
127
|
+
return this
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export default FeaturedImageUpload
|