@nextsparkjs/theme-blog 0.1.0-beta.17 → 0.1.0-beta.171

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 (49) hide show
  1. package/api/authors/[username]/route.ts +5 -2
  2. package/api/authors/docs.md +135 -0
  3. package/api/authors/presets.ts +45 -0
  4. package/api/authors/route.ts +4 -1
  5. package/api/posts/public/docs.md +124 -0
  6. package/api/posts/public/presets.ts +65 -0
  7. package/api/posts/public/route.ts +4 -1
  8. package/config/app.config.ts +4 -5
  9. package/config/billing.config.ts +4 -7
  10. package/config/dashboard.config.ts +13 -0
  11. package/config/permissions.config.ts +11 -0
  12. package/entities/categories/api/docs.md +119 -0
  13. package/entities/categories/api/presets.ts +67 -0
  14. package/entities/posts/api/docs.md +174 -0
  15. package/entities/posts/api/presets.ts +137 -0
  16. package/lib/selectors.ts +2 -3
  17. package/nextsparkjs-theme-blog-0.1.0-beta.137.tgz +0 -0
  18. package/package.json +4 -3
  19. package/styles/globals.css +45 -0
  20. package/tests/cypress/e2e/README.md +170 -0
  21. package/tests/cypress/e2e/categories/categories-crud.cy.ts +322 -0
  22. package/tests/cypress/e2e/categories/categories-crud.md +73 -0
  23. package/tests/cypress/e2e/posts/posts-crud.cy.ts +460 -0
  24. package/tests/cypress/e2e/posts/posts-crud.md +115 -0
  25. package/tests/cypress/e2e/posts/posts-editor.cy.ts +290 -0
  26. package/tests/cypress/e2e/posts/posts-editor.md +139 -0
  27. package/tests/cypress/e2e/posts/posts-status-workflow.cy.ts +302 -0
  28. package/tests/cypress/e2e/posts/posts-status-workflow.md +83 -0
  29. package/tests/cypress/fixtures/blocks.json +9 -0
  30. package/tests/cypress/fixtures/entities.json +42 -0
  31. package/tests/cypress/src/FeaturedImageUpload.js +131 -0
  32. package/tests/cypress/src/PostEditor.js +386 -0
  33. package/tests/cypress/src/PostsList.js +350 -0
  34. package/tests/cypress/src/WysiwygEditor.js +373 -0
  35. package/tests/cypress/src/components/EntityForm.ts +378 -0
  36. package/tests/cypress/src/components/EntityList.ts +378 -0
  37. package/tests/cypress/src/components/PostEditorPOM.ts +447 -0
  38. package/tests/cypress/src/components/PostsPOM.ts +362 -0
  39. package/tests/cypress/src/components/index.ts +18 -0
  40. package/tests/cypress/src/index.js +33 -0
  41. package/tests/cypress/src/selectors.ts +49 -0
  42. package/tests/cypress/src/session-helpers.ts +151 -0
  43. package/tests/cypress/support/e2e.ts +90 -0
  44. package/tests/cypress.config.ts +154 -0
  45. package/tests/jest/__mocks__/jose.js +22 -0
  46. package/tests/jest/__mocks__/next-server.js +56 -0
  47. package/tests/jest/jest.config.cjs +131 -0
  48. package/tests/jest/setup.ts +170 -0
  49. package/tests/tsconfig.json +15 -0
@@ -0,0 +1,322 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Categories CRUD Tests - Blog Theme
5
+ *
6
+ * Tests for Categories entity CRUD operations.
7
+ * Uses generic EntityList and EntityForm POM classes.
8
+ *
9
+ * Theme Mode: single-user (isolated blogs, no team collaboration)
10
+ */
11
+
12
+ import { EntityList } from '../../src/components/EntityList'
13
+ import { EntityForm } from '../../src/components/EntityForm'
14
+ import { loginAsBlogAuthor } from '../../src/session-helpers'
15
+
16
+ describe('Categories CRUD - Blog Author (Full Access)', () => {
17
+ const categoryList = new EntityList('categories')
18
+ const categoryForm = new EntityForm('categories')
19
+
20
+ beforeEach(() => {
21
+ loginAsBlogAuthor('MARCOS')
22
+ cy.visit('/dashboard/categories')
23
+ categoryList.validateListVisible()
24
+ })
25
+
26
+ // =========================================================================
27
+ // CREATE - Author can create categories
28
+ // =========================================================================
29
+ describe('CREATE - Author can create categories', () => {
30
+ it('BLOG_CAT_CREATE_001: should create new category successfully', () => {
31
+ const timestamp = Date.now()
32
+ const categoryName = `Category ${timestamp}`
33
+ const categorySlug = `category-${timestamp}`
34
+
35
+ // Click create button
36
+ categoryList.clickAdd()
37
+
38
+ // Validate form is visible
39
+ categoryForm.validateFormVisible()
40
+
41
+ // Fill required fields (name and slug are both required)
42
+ categoryForm.fillField('name', categoryName)
43
+ categoryForm.fillField('slug', categorySlug)
44
+
45
+ // Submit form
46
+ categoryForm.submit()
47
+
48
+ // Validate redirect to list
49
+ cy.url().should('include', '/dashboard/categories')
50
+
51
+ // Validate category appears in list
52
+ cy.contains(categoryName).should('be.visible')
53
+
54
+ cy.log('✅ Author created category successfully')
55
+ })
56
+
57
+ it('BLOG_CAT_CREATE_002: should create category with description', () => {
58
+ const timestamp = Date.now()
59
+ const categoryName = `Full Category ${timestamp}`
60
+ const categorySlug = `full-category-${timestamp}`
61
+ const categoryDescription = 'This is a category with a description'
62
+
63
+ // Click create button
64
+ categoryList.clickAdd()
65
+
66
+ // Fill all fields (name and slug are required)
67
+ categoryForm.fillField('name', categoryName)
68
+ categoryForm.fillField('slug', categorySlug)
69
+ categoryForm.fillField('description', categoryDescription)
70
+
71
+ // Submit form
72
+ categoryForm.submit()
73
+
74
+ // Validate category appears in list
75
+ cy.url().should('include', '/dashboard/categories')
76
+ cy.contains(categoryName).should('be.visible')
77
+
78
+ cy.log('✅ Author created category with description successfully')
79
+ })
80
+
81
+ it('BLOG_CAT_CREATE_003: should show validation error for empty name', () => {
82
+ // Click create button
83
+ categoryList.clickAdd()
84
+
85
+ // Try to submit without filling name
86
+ categoryForm.submit()
87
+
88
+ // Validate error is shown (form should have validation)
89
+ cy.get('body').then($body => {
90
+ // Check for validation message or form stays on same page
91
+ const isOnFormPage = $body.find('[data-cy="categories-form"]').length > 0 ||
92
+ $body.find('[data-cy="entity-form"]').length > 0
93
+
94
+ if (isOnFormPage) {
95
+ cy.log('✅ Form validation prevented submission')
96
+ } else {
97
+ // Check for error message
98
+ cy.get('[role="alert"], .text-destructive, [data-cy*="error"]')
99
+ .should('be.visible')
100
+ cy.log('✅ Validation error shown for empty name')
101
+ }
102
+ })
103
+ })
104
+ })
105
+
106
+ // =========================================================================
107
+ // READ - Author can view categories
108
+ // =========================================================================
109
+ describe('READ - Author can view categories', () => {
110
+ it('BLOG_CAT_READ_001: should view categories list', () => {
111
+ // Validate list is visible
112
+ categoryList.validateListVisible()
113
+
114
+ cy.log('✅ Author can view categories list')
115
+ })
116
+
117
+ it('BLOG_CAT_READ_002: should search categories', () => {
118
+ // Create a category with unique name first
119
+ const timestamp = Date.now()
120
+ const uniqueName = `Searchable Cat ${timestamp}`
121
+ const uniqueSlug = `searchable-cat-${timestamp}`
122
+
123
+ categoryList.clickAdd()
124
+ categoryForm.fillField('name', uniqueName)
125
+ categoryForm.fillField('slug', uniqueSlug)
126
+ categoryForm.submit()
127
+
128
+ // Go back to list
129
+ cy.visit('/dashboard/categories')
130
+ categoryList.validateListVisible()
131
+
132
+ // Search for the category
133
+ categoryList.search(uniqueName.substring(0, 10))
134
+ cy.wait(500) // Wait for search
135
+
136
+ // Validate search results
137
+ cy.contains(uniqueName).should('be.visible')
138
+
139
+ // Clear search
140
+ categoryList.clearSearch()
141
+
142
+ cy.log('✅ Author can search categories')
143
+ })
144
+ })
145
+
146
+ // =========================================================================
147
+ // UPDATE - Author can update categories
148
+ // =========================================================================
149
+ describe('UPDATE - Author can update categories', () => {
150
+ beforeEach(() => {
151
+ // Create a test category for update tests
152
+ const timestamp = Date.now()
153
+ const testName = `Update Cat ${timestamp}`
154
+ const testSlug = `update-cat-${timestamp}`
155
+
156
+ categoryList.clickAdd()
157
+ categoryForm.fillField('name', testName)
158
+ categoryForm.fillField('slug', testSlug)
159
+ categoryForm.submit()
160
+
161
+ cy.visit('/dashboard/categories')
162
+ categoryList.validateListVisible()
163
+ })
164
+
165
+ it('BLOG_CAT_UPDATE_001: should edit category name', () => {
166
+ // Find and edit the first category
167
+ cy.get('body').then($body => {
168
+ const editSelector = '[data-cy*="edit"]'
169
+
170
+ if ($body.find(editSelector).length > 0) {
171
+ cy.get(editSelector).first().click()
172
+
173
+ // Validate form is visible
174
+ categoryForm.validateFormVisible()
175
+
176
+ // Update name
177
+ const updatedName = `Updated Category ${Date.now()}`
178
+ categoryForm.fillField('name', updatedName)
179
+
180
+ // Submit form
181
+ categoryForm.submit()
182
+
183
+ // Validate update
184
+ cy.url().should('include', '/dashboard/categories')
185
+ cy.contains(updatedName).should('be.visible')
186
+
187
+ cy.log('✅ Author updated category name successfully')
188
+ } else {
189
+ cy.log('⚠️ No categories available to edit')
190
+ }
191
+ })
192
+ })
193
+
194
+ it('BLOG_CAT_UPDATE_002: should update category description', () => {
195
+ cy.get('body').then($body => {
196
+ const editSelector = '[data-cy*="edit"]'
197
+
198
+ if ($body.find(editSelector).length > 0) {
199
+ cy.get(editSelector).first().click()
200
+
201
+ categoryForm.validateFormVisible()
202
+
203
+ // Update description
204
+ const updatedDescription = `Updated description ${Date.now()}`
205
+ categoryForm.fillField('description', updatedDescription)
206
+
207
+ // Submit form
208
+ categoryForm.submit()
209
+
210
+ cy.url().should('include', '/dashboard/categories')
211
+
212
+ cy.log('✅ Author updated category description successfully')
213
+ } else {
214
+ cy.log('⚠️ No categories available to edit')
215
+ }
216
+ })
217
+ })
218
+ })
219
+
220
+ // =========================================================================
221
+ // DELETE - Author can delete categories
222
+ // =========================================================================
223
+ describe('DELETE - Author can delete categories', () => {
224
+ it('BLOG_CAT_DELETE_001: should delete category successfully', () => {
225
+ // Create a category to delete
226
+ const timestamp = Date.now()
227
+ const categoryName = `Delete Cat ${timestamp}`
228
+ const categorySlug = `delete-cat-${timestamp}`
229
+
230
+ categoryList.clickAdd()
231
+ categoryForm.fillField('name', categoryName)
232
+ categoryForm.fillField('slug', categorySlug)
233
+ categoryForm.submit()
234
+
235
+ // Wait for category to appear in list
236
+ cy.visit('/dashboard/categories')
237
+ cy.contains(categoryName).should('be.visible')
238
+
239
+ // Find and delete
240
+ cy.get('body').then($body => {
241
+ const deleteSelector = '[data-cy*="delete"]'
242
+
243
+ if ($body.find(deleteSelector).length > 0) {
244
+ cy.get(deleteSelector).first().click()
245
+
246
+ // Confirm deletion if modal appears
247
+ cy.get('body').then($body2 => {
248
+ if ($body2.find('[data-cy="confirm-delete"]').length > 0) {
249
+ cy.get('[data-cy="confirm-delete"]').click()
250
+ } else if ($body2.find('[data-cy*="delete-confirm"]').length > 0) {
251
+ cy.get('[data-cy*="delete-confirm"]').click()
252
+ } else if ($body2.find('[role="dialog"] button[data-cy="confirm-delete"]').length > 0) {
253
+ // Click the delete button in the modal (not the trigger)
254
+ cy.get('[role="dialog"] button[data-cy="confirm-delete"]').click()
255
+ } else if ($body2.find('[role="dialog"]').length > 0) {
256
+ // Fallback: click any button that looks like confirm delete in the dialog
257
+ cy.get('[role="dialog"] button').last().click()
258
+ }
259
+ })
260
+
261
+ // Validate deletion
262
+ cy.wait(500)
263
+ cy.contains(categoryName).should('not.exist')
264
+
265
+ cy.log('✅ Author deleted category successfully')
266
+ } else {
267
+ cy.log('⚠️ Delete button not found')
268
+ }
269
+ })
270
+ })
271
+
272
+ it('BLOG_CAT_DELETE_002: should cancel delete operation', () => {
273
+ // Create a category
274
+ const timestamp = Date.now()
275
+ const categoryName = `Cancel Delete Cat ${timestamp}`
276
+ const categorySlug = `cancel-delete-cat-${timestamp}`
277
+
278
+ categoryList.clickAdd()
279
+ categoryForm.fillField('name', categoryName)
280
+ categoryForm.fillField('slug', categorySlug)
281
+ categoryForm.submit()
282
+
283
+ // Wait for category to appear
284
+ cy.visit('/dashboard/categories')
285
+ cy.contains(categoryName).should('be.visible')
286
+
287
+ // Find and try to delete, then cancel
288
+ cy.get('body').then($body => {
289
+ const deleteSelector = '[data-cy*="delete"]'
290
+
291
+ if ($body.find(deleteSelector).length > 0) {
292
+ cy.get(deleteSelector).first().click()
293
+
294
+ // Cancel deletion if modal appears
295
+ cy.get('body').then($body2 => {
296
+ if ($body2.find('[data-cy="cancel-delete"]').length > 0) {
297
+ cy.get('[data-cy="cancel-delete"]').click()
298
+ } else if ($body2.find('[data-cy*="delete-cancel"]').length > 0) {
299
+ cy.get('[data-cy*="delete-cancel"]').click()
300
+ } else if ($body2.find('[role="dialog"] button[data-cy="cancel-delete"]').length > 0) {
301
+ cy.get('[role="dialog"] button[data-cy="cancel-delete"]').click()
302
+ } else if ($body2.find('[role="dialog"]').length > 0) {
303
+ // Fallback: click the first button (usually cancel) in the dialog
304
+ cy.get('[role="dialog"] button').first().click()
305
+ }
306
+ })
307
+
308
+ // Validate category still exists
309
+ cy.contains(categoryName).should('be.visible')
310
+
311
+ cy.log('✅ Author cancelled delete operation successfully')
312
+ } else {
313
+ cy.log('⚠️ Delete button not found')
314
+ }
315
+ })
316
+ })
317
+ })
318
+
319
+ after(() => {
320
+ cy.log('✅ Categories CRUD tests completed')
321
+ })
322
+ })
@@ -0,0 +1,73 @@
1
+ # Categories CRUD - E2E Tests
2
+
3
+ ## Overview
4
+
5
+ Tests for Categories entity CRUD operations in the Blog theme.
6
+
7
+ **Test File:** `test/cypress/e2e/themes/blog/categories/categories-crud.cy.ts`
8
+
9
+ ## Entity Characteristics
10
+
11
+ | Property | Value |
12
+ |----------|-------|
13
+ | **Entity** | Categories |
14
+ | **UI** | Generic (EntityList, EntityForm) |
15
+ | **Team Mode** | Single-user (isolated) |
16
+ | **Fields** | name, slug, description |
17
+
18
+ ## Test Coverage
19
+
20
+ ### 1. CREATE (3 tests)
21
+
22
+ | ID | Test Case | Description | Status |
23
+ |----|-----------|-------------|--------|
24
+ | BLOG_CAT_CREATE_001 | Create category | Create with name and slug | ✅ Passing |
25
+ | BLOG_CAT_CREATE_002 | Create with description | Create with all fields | ✅ Passing |
26
+ | BLOG_CAT_CREATE_003 | Validation empty name | Show error for empty name | ✅ Passing |
27
+
28
+ ### 2. READ (2 tests)
29
+
30
+ | ID | Test Case | Description | Status |
31
+ |----|-----------|-------------|--------|
32
+ | BLOG_CAT_READ_001 | View categories list | Display list of categories | ✅ Passing |
33
+ | BLOG_CAT_READ_002 | Search categories | Search by name | ✅ Passing |
34
+
35
+ ### 3. UPDATE (2 tests)
36
+
37
+ | ID | Test Case | Description | Status |
38
+ |----|-----------|-------------|--------|
39
+ | BLOG_CAT_UPDATE_001 | Edit name | Update category name | ✅ Passing |
40
+ | BLOG_CAT_UPDATE_002 | Edit description | Update description | ✅ Passing |
41
+
42
+ ### 4. DELETE (2 tests)
43
+
44
+ | ID | Test Case | Description | Status |
45
+ |----|-----------|-------------|--------|
46
+ | BLOG_CAT_DELETE_001 | Delete category | Confirm and delete | ✅ Passing |
47
+ | BLOG_CAT_DELETE_002 | Cancel delete | Cancel delete operation | ✅ Passing |
48
+
49
+ ## Summary
50
+
51
+ | Category | Total | Passing | Pending | Failing |
52
+ |----------|-------|---------|---------|---------|
53
+ | CREATE | 3 | 3 | 0 | 0 |
54
+ | READ | 2 | 2 | 0 | 0 |
55
+ | UPDATE | 2 | 2 | 0 | 0 |
56
+ | DELETE | 2 | 2 | 0 | 0 |
57
+ | **Total** | **9** | **9** | **0** | **0** |
58
+
59
+ ## POM Classes
60
+
61
+ Uses generic entity classes:
62
+ - **EntityList:** `test/cypress/src/classes/components/entities/EntityList.js`
63
+ - **EntityForm:** `test/cypress/src/classes/components/entities/EntityForm.js`
64
+
65
+ ## Running Tests
66
+
67
+ ```bash
68
+ npx cypress run --spec "test/cypress/e2e/themes/blog/categories/categories-crud.cy.ts"
69
+ ```
70
+
71
+ ---
72
+
73
+ **Last Updated:** 2025-12-04