@nextsparkjs/theme-default 0.1.0-beta.2 → 0.1.0-beta.21
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 +8 -4
- package/templates/(public)/page.tsx +1 -1
- package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
- package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
- package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
- package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
- package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
- package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
- package/tests/cypress/e2e/_docs/README.md +215 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
- package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
- package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
- package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
- package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
- package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
- package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
- package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
- package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
- package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
- package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
- package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
- package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
- package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
- package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
- package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
- package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
- package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
- package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
- package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
- package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
- package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
- package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
- package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
- package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
- package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
- package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
- package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
- package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
- package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
- package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
- package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
- package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
- package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
- package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
- package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
- package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
- package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
- package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
- package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
- package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
- package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
- package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
- package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
- package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
- package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
- package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
- package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
- package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
- package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
- package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
- package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
- package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
- package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
- package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
- package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
- package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
- package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
- package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
- package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
- package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
- package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
- package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
- package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
- package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
- package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
- package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
- package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
- package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
- package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
- package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
- package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
- package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
- package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
- package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
- package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
- package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
- package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
- package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
- package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
- package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
- package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
- package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
- package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
- package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
- package/tests/cypress/fixtures/blocks.json +218 -0
- package/tests/cypress/fixtures/entities.json +78 -0
- package/tests/cypress/fixtures/page-builder.json +21 -0
- package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
- package/tests/cypress/src/components/CustomersPOM.ts +439 -0
- package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
- package/tests/cypress/src/components/EntityForm.ts +375 -0
- package/tests/cypress/src/components/EntityList.ts +389 -0
- package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
- package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
- package/tests/cypress/src/components/PostsListPOM.ts +223 -0
- package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
- package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
- package/tests/cypress/src/components/TasksPOM.ts +272 -0
- package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
- package/tests/cypress/src/components/index.ts +21 -0
- package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
- package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
- package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
- package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
- package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
- package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
- package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
- package/tests/cypress/src/controllers/index.js +25 -0
- package/tests/cypress/src/core/AuthPOM.ts +450 -0
- package/tests/cypress/src/core/BasePOM.ts +86 -0
- package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
- package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
- package/tests/cypress/src/core/index.ts +14 -0
- package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
- package/tests/cypress/src/entities/PagesPOM.ts +137 -0
- package/tests/cypress/src/entities/PostsPOM.ts +137 -0
- package/tests/cypress/src/entities/TasksPOM.ts +176 -0
- package/tests/cypress/src/entities/index.ts +14 -0
- package/tests/cypress/src/features/BillingPOM.ts +385 -0
- package/tests/cypress/src/features/DashboardPOM.ts +245 -0
- package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
- package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
- package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
- package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
- package/tests/cypress/src/features/SettingsPOM.ts +362 -0
- package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
- package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
- package/tests/cypress/src/features/index.ts +28 -0
- package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
- package/tests/cypress/src/index.ts +101 -0
- package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
- package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
- package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
- package/tests/cypress/src/pages/dashboard/index.js +6 -0
- package/tests/cypress/src/pages/index.js +5 -0
- package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
- package/tests/cypress/src/pages/public/LandingPage.js +69 -0
- package/tests/cypress/src/pages/public/PricingPage.js +33 -0
- package/tests/cypress/src/pages/public/index.js +6 -0
- package/tests/cypress/src/selectors.ts +46 -0
- package/tests/cypress/src/session-helpers.ts +500 -0
- package/tests/cypress/support/doc-commands.ts +260 -0
- package/tests/cypress.config.ts +150 -0
- package/tests/jest/components/post-header.test.tsx +377 -0
- package/tests/jest/config/role-config.test.ts +529 -0
- package/tests/jest/jest.config.ts +81 -0
- package/tests/jest/langchain/COVERAGE.md +372 -0
- package/tests/jest/langchain/guardrails.test.ts +465 -0
- package/tests/jest/langchain/streaming.test.ts +367 -0
- package/tests/jest/langchain/token-tracker.test.ts +455 -0
- package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
- package/tests/jest/langchain/tracer.test.ts +823 -0
- package/tests/jest/user-roles/role-helpers.test.ts +432 -0
- package/tests/jest/validation/categories.test.ts +429 -0
- package/tests/jest/validation/posts.test.ts +546 -0
- package/tests/tsconfig.json +15 -0
- package/LICENSE +0 -21
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Posts API - CRUD Tests
|
|
5
|
+
*
|
|
6
|
+
* Comprehensive test suite for Posts API endpoints.
|
|
7
|
+
* Tests GET, POST, PUT, DELETE operations with taxonomy relations.
|
|
8
|
+
*
|
|
9
|
+
* Entity characteristics:
|
|
10
|
+
* - Required fields: slug, title, blocks, locale
|
|
11
|
+
* - Optional fields: excerpt, featuredImage, SEO fields, categoryIds
|
|
12
|
+
* - Unique constraint: slug + locale
|
|
13
|
+
* - Relations: post_taxonomy_relations (many-to-many with taxonomies)
|
|
14
|
+
* - Access: authenticated users only (dual auth: session + API key)
|
|
15
|
+
*
|
|
16
|
+
* Tags: @api, @feat-posts, @crud, @regression
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import * as allure from 'allure-cypress'
|
|
20
|
+
|
|
21
|
+
const PostsAPIController = require('../../src/controllers/PostsAPIController.js')
|
|
22
|
+
|
|
23
|
+
describe('Posts API - CRUD Operations', {
|
|
24
|
+
tags: ['@api', '@feat-posts', '@crud', '@regression']
|
|
25
|
+
}, () => {
|
|
26
|
+
// Test constants
|
|
27
|
+
const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
|
|
28
|
+
const TEAM_ID = 'team-tmt-001'
|
|
29
|
+
const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
|
|
30
|
+
|
|
31
|
+
// Controller instance
|
|
32
|
+
let postsAPI: InstanceType<typeof PostsAPIController>
|
|
33
|
+
|
|
34
|
+
// Track created posts for cleanup
|
|
35
|
+
let createdPosts: string[] = []
|
|
36
|
+
|
|
37
|
+
before(() => {
|
|
38
|
+
// Initialize controller with superadmin credentials and team context
|
|
39
|
+
postsAPI = new PostsAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
|
|
40
|
+
cy.log('PostsAPIController initialized')
|
|
41
|
+
cy.log(`Base URL: ${BASE_URL}`)
|
|
42
|
+
cy.log(`Team ID: ${TEAM_ID}`)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
allure.epic('API')
|
|
47
|
+
allure.feature('Posts')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
// Cleanup: Delete posts created during tests
|
|
52
|
+
createdPosts.forEach((postId) => {
|
|
53
|
+
postsAPI.deletePost(postId)
|
|
54
|
+
})
|
|
55
|
+
createdPosts = []
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// ============================================================
|
|
59
|
+
// GET /api/v1/posts - List Posts
|
|
60
|
+
// ============================================================
|
|
61
|
+
describe('GET /api/v1/posts - List Posts', () => {
|
|
62
|
+
it('POST_API_001: Should list posts with valid API key', { tags: '@smoke' }, () => {
|
|
63
|
+
allure.story('CRUD Operations')
|
|
64
|
+
allure.severity('critical')
|
|
65
|
+
|
|
66
|
+
postsAPI.getPosts().then((response: any) => {
|
|
67
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
68
|
+
postsAPI.validatePaginatedResponse(response)
|
|
69
|
+
expect(response.body.data).to.be.an('array')
|
|
70
|
+
|
|
71
|
+
cy.log(`Found ${response.body.data.length} posts`)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('POST_API_002: Should return post with expected structure', () => {
|
|
76
|
+
postsAPI.getPosts().then((response: any) => {
|
|
77
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
78
|
+
|
|
79
|
+
if (response.body.data.length > 0) {
|
|
80
|
+
const post = response.body.data[0]
|
|
81
|
+
|
|
82
|
+
// Verify post structure
|
|
83
|
+
expect(post).to.have.property('id')
|
|
84
|
+
expect(post).to.have.property('slug')
|
|
85
|
+
expect(post).to.have.property('title')
|
|
86
|
+
expect(post).to.have.property('locale')
|
|
87
|
+
expect(post).to.have.property('blocks')
|
|
88
|
+
expect(post.blocks).to.be.an('array')
|
|
89
|
+
|
|
90
|
+
// Categories relation (if present)
|
|
91
|
+
if (post.hasOwnProperty('categories')) {
|
|
92
|
+
expect(post.categories).to.be.an('array')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
cy.log(`Post structure verified: ${post.title}`)
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('POST_API_003: Should paginate posts', () => {
|
|
101
|
+
postsAPI.getPosts({ page: 1, limit: 5 }).then((response: any) => {
|
|
102
|
+
postsAPI.validatePaginatedResponse(response)
|
|
103
|
+
expect(response.body.info.page).to.eq(1)
|
|
104
|
+
expect(response.body.info.limit).to.eq(5)
|
|
105
|
+
expect(response.body.data.length).to.be.at.most(5)
|
|
106
|
+
|
|
107
|
+
cy.log(`Page 1 with limit 5: ${response.body.data.length} posts`)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('POST_API_004: Should filter posts by published status', () => {
|
|
112
|
+
postsAPI.getPosts({ published: true }).then((response: any) => {
|
|
113
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
114
|
+
expect(response.body.data).to.be.an('array')
|
|
115
|
+
|
|
116
|
+
// All returned posts should be published
|
|
117
|
+
response.body.data.forEach((post: any) => {
|
|
118
|
+
expect(post.published).to.be.true
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
cy.log(`Found ${response.body.data.length} published posts`)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('POST_API_005: Should filter posts by locale', () => {
|
|
126
|
+
postsAPI.getPosts({ locale: 'en' }).then((response: any) => {
|
|
127
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
128
|
+
expect(response.body.data).to.be.an('array')
|
|
129
|
+
|
|
130
|
+
// All returned posts should have locale 'en'
|
|
131
|
+
response.body.data.forEach((post: any) => {
|
|
132
|
+
expect(post.locale).to.eq('en')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
cy.log(`Found ${response.body.data.length} posts in 'en' locale`)
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('POST_API_006: Should search posts by title/excerpt', () => {
|
|
140
|
+
// Create a post with unique searchable term
|
|
141
|
+
const uniqueTerm = `SearchPost${Date.now()}`
|
|
142
|
+
const postData = PostsAPIController.generateRandomPostData({
|
|
143
|
+
title: `Post ${uniqueTerm} Title`,
|
|
144
|
+
excerpt: 'Test excerpt'
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
postsAPI.createPost(postData).then((createResponse: any) => {
|
|
148
|
+
postsAPI.validateSuccessResponse(createResponse, 201)
|
|
149
|
+
createdPosts.push(createResponse.body.data.id)
|
|
150
|
+
|
|
151
|
+
// Search for the unique term
|
|
152
|
+
postsAPI.getPosts({ search: uniqueTerm }).then((response: any) => {
|
|
153
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
154
|
+
expect(response.body.data).to.be.an('array')
|
|
155
|
+
expect(response.body.data.length).to.be.greaterThan(0)
|
|
156
|
+
|
|
157
|
+
const foundPost = response.body.data.find(
|
|
158
|
+
(p: any) => p.id === createResponse.body.data.id
|
|
159
|
+
)
|
|
160
|
+
expect(foundPost).to.exist
|
|
161
|
+
expect(foundPost.title).to.include(uniqueTerm)
|
|
162
|
+
|
|
163
|
+
cy.log(`Search found ${response.body.data.length} posts matching '${uniqueTerm}'`)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('POST_API_007: Should reject request without authentication', { tags: '@security' }, () => {
|
|
169
|
+
// Create controller without API key
|
|
170
|
+
const noAuthAPI = new PostsAPIController(BASE_URL, null, TEAM_ID)
|
|
171
|
+
|
|
172
|
+
noAuthAPI.getPosts().then((response: any) => {
|
|
173
|
+
expect(response.status).to.eq(401)
|
|
174
|
+
expect(response.body.success).to.be.false
|
|
175
|
+
|
|
176
|
+
cy.log('Request without API key rejected with 401')
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('POST_API_008: Should reject request without x-team-id', () => {
|
|
181
|
+
// Create controller without team ID
|
|
182
|
+
const noTeamAPI = new PostsAPIController(BASE_URL, SUPERADMIN_API_KEY, null)
|
|
183
|
+
|
|
184
|
+
noTeamAPI.getPosts().then((response: any) => {
|
|
185
|
+
expect(response.status).to.eq(400)
|
|
186
|
+
expect(response.body.success).to.be.false
|
|
187
|
+
expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
|
|
188
|
+
|
|
189
|
+
cy.log('Request without x-team-id rejected with TEAM_CONTEXT_REQUIRED')
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
// ============================================================
|
|
195
|
+
// POST /api/v1/posts - Create Post
|
|
196
|
+
// ============================================================
|
|
197
|
+
describe('POST /api/v1/posts - Create Post', () => {
|
|
198
|
+
it('POST_API_010: Should create post with valid data', { tags: '@smoke' }, () => {
|
|
199
|
+
allure.story('CRUD Operations')
|
|
200
|
+
allure.severity('critical')
|
|
201
|
+
|
|
202
|
+
const postData = PostsAPIController.generateRandomPostData({
|
|
203
|
+
title: 'TestCreate - Full Post',
|
|
204
|
+
excerpt: 'This is a test post excerpt',
|
|
205
|
+
featuredImage: '/images/test-featured.jpg',
|
|
206
|
+
blocks: [
|
|
207
|
+
{
|
|
208
|
+
id: `block-${Date.now()}`,
|
|
209
|
+
blockSlug: 'hero',
|
|
210
|
+
props: {
|
|
211
|
+
title: 'Test Hero',
|
|
212
|
+
subtitle: 'Test Subtitle'
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
postsAPI.createPost(postData).then((response: any) => {
|
|
219
|
+
postsAPI.validateSuccessResponse(response, 201)
|
|
220
|
+
postsAPI.validatePostObject(response.body.data)
|
|
221
|
+
|
|
222
|
+
const post = response.body.data
|
|
223
|
+
expect(post.slug).to.eq(postData.slug)
|
|
224
|
+
expect(post.title).to.eq(postData.title)
|
|
225
|
+
expect(post.excerpt).to.eq(postData.excerpt)
|
|
226
|
+
expect(post.featuredImage).to.eq(postData.featuredImage)
|
|
227
|
+
expect(post.locale).to.eq(postData.locale)
|
|
228
|
+
expect(post.blocks).to.have.length(1)
|
|
229
|
+
|
|
230
|
+
createdPosts.push(post.id)
|
|
231
|
+
cy.log(`Created post: ${post.title} (ID: ${post.id})`)
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('POST_API_011: Should create post with minimal data', () => {
|
|
236
|
+
const minimalData = PostsAPIController.generateRandomPostData({
|
|
237
|
+
title: `Minimal Post ${Date.now()}`
|
|
238
|
+
})
|
|
239
|
+
delete minimalData.excerpt
|
|
240
|
+
delete minimalData.categoryIds
|
|
241
|
+
|
|
242
|
+
postsAPI.createPost(minimalData).then((response: any) => {
|
|
243
|
+
postsAPI.validateSuccessResponse(response, 201)
|
|
244
|
+
postsAPI.validatePostObject(response.body.data)
|
|
245
|
+
|
|
246
|
+
const post = response.body.data
|
|
247
|
+
expect(post.slug).to.eq(minimalData.slug)
|
|
248
|
+
expect(post.title).to.eq(minimalData.title)
|
|
249
|
+
expect(post.locale).to.eq(minimalData.locale)
|
|
250
|
+
|
|
251
|
+
createdPosts.push(post.id)
|
|
252
|
+
cy.log(`Created minimal post: ${post.id}`)
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('POST_API_012: Should create post with SEO fields', () => {
|
|
257
|
+
const postData = PostsAPIController.generateRandomPostData({
|
|
258
|
+
title: `SEO Post ${Date.now()}`,
|
|
259
|
+
seoTitle: 'Custom SEO Title',
|
|
260
|
+
seoDescription: 'Custom SEO Description',
|
|
261
|
+
seoKeywords: 'test, seo, keywords',
|
|
262
|
+
ogImage: '/images/og-image.jpg',
|
|
263
|
+
noindex: false,
|
|
264
|
+
nofollow: false
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
postsAPI.createPost(postData).then((response: any) => {
|
|
268
|
+
postsAPI.validateSuccessResponse(response, 201)
|
|
269
|
+
|
|
270
|
+
const post = response.body.data
|
|
271
|
+
expect(post.seoTitle).to.eq(postData.seoTitle)
|
|
272
|
+
expect(post.seoDescription).to.eq(postData.seoDescription)
|
|
273
|
+
expect(post.seoKeywords).to.eq(postData.seoKeywords)
|
|
274
|
+
expect(post.ogImage).to.eq(postData.ogImage)
|
|
275
|
+
|
|
276
|
+
createdPosts.push(post.id)
|
|
277
|
+
cy.log(`Created post with SEO fields: ${post.id}`)
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('POST_API_013: Should reject creation without title', () => {
|
|
282
|
+
const invalidData = {
|
|
283
|
+
slug: `no-title-${Date.now()}`,
|
|
284
|
+
locale: 'en',
|
|
285
|
+
blocks: []
|
|
286
|
+
// Missing: title
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
postsAPI.createPost(invalidData).then((response: any) => {
|
|
290
|
+
postsAPI.validateErrorResponse(response, 400)
|
|
291
|
+
|
|
292
|
+
cy.log('Creation without title rejected with 400')
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('POST_API_014: Should reject creation without slug', () => {
|
|
297
|
+
const invalidData = {
|
|
298
|
+
title: 'Test Post',
|
|
299
|
+
locale: 'en',
|
|
300
|
+
blocks: []
|
|
301
|
+
// Missing: slug
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
postsAPI.createPost(invalidData).then((response: any) => {
|
|
305
|
+
postsAPI.validateErrorResponse(response, 400)
|
|
306
|
+
|
|
307
|
+
cy.log('Creation without slug rejected with 400')
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it('POST_API_015: Should reject invalid slug format', () => {
|
|
312
|
+
const invalidData = {
|
|
313
|
+
slug: 'Invalid Slug With Spaces!',
|
|
314
|
+
title: 'Test Post',
|
|
315
|
+
locale: 'en',
|
|
316
|
+
blocks: []
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
postsAPI.createPost(invalidData).then((response: any) => {
|
|
320
|
+
postsAPI.validateErrorResponse(response, 400)
|
|
321
|
+
|
|
322
|
+
cy.log('Invalid slug format rejected with 400')
|
|
323
|
+
})
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
it('POST_API_016: Should reject duplicate slug in same locale', () => {
|
|
327
|
+
const uniqueSlug = `duplicate-slug-${Date.now()}`
|
|
328
|
+
|
|
329
|
+
// Create first post
|
|
330
|
+
const postData1 = PostsAPIController.generateRandomPostData({
|
|
331
|
+
slug: uniqueSlug,
|
|
332
|
+
title: 'First Post'
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
postsAPI.createPost(postData1).then((response1: any) => {
|
|
336
|
+
postsAPI.validateSuccessResponse(response1, 201)
|
|
337
|
+
createdPosts.push(response1.body.data.id)
|
|
338
|
+
|
|
339
|
+
// Try to create duplicate
|
|
340
|
+
const postData2 = PostsAPIController.generateRandomPostData({
|
|
341
|
+
slug: uniqueSlug, // Same slug
|
|
342
|
+
title: 'Second Post'
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
postsAPI.createPost(postData2).then((response2: any) => {
|
|
346
|
+
expect(response2.status).to.be.oneOf([400, 409])
|
|
347
|
+
expect(response2.body.success).to.be.false
|
|
348
|
+
|
|
349
|
+
cy.log('Duplicate slug in same locale rejected')
|
|
350
|
+
})
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('POST_API_017: Should allow same slug in different locale', () => {
|
|
355
|
+
const sharedSlug = `shared-slug-${Date.now()}`
|
|
356
|
+
|
|
357
|
+
// Create post in 'en' locale
|
|
358
|
+
const postDataEN = PostsAPIController.generateRandomPostData({
|
|
359
|
+
slug: sharedSlug,
|
|
360
|
+
title: 'English Post',
|
|
361
|
+
locale: 'en'
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
postsAPI.createPost(postDataEN).then((responseEN: any) => {
|
|
365
|
+
postsAPI.validateSuccessResponse(responseEN, 201)
|
|
366
|
+
createdPosts.push(responseEN.body.data.id)
|
|
367
|
+
|
|
368
|
+
// Create post with same slug in 'es' locale
|
|
369
|
+
const postDataES = PostsAPIController.generateRandomPostData({
|
|
370
|
+
slug: sharedSlug, // Same slug
|
|
371
|
+
title: 'Spanish Post',
|
|
372
|
+
locale: 'es' // Different locale
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
postsAPI.createPost(postDataES).then((responseES: any) => {
|
|
376
|
+
postsAPI.validateSuccessResponse(responseES, 201)
|
|
377
|
+
createdPosts.push(responseES.body.data.id)
|
|
378
|
+
|
|
379
|
+
cy.log('Same slug in different locale allowed')
|
|
380
|
+
})
|
|
381
|
+
})
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('POST_API_018: Should reject creation without authentication', { tags: '@security' }, () => {
|
|
385
|
+
const noAuthAPI = new PostsAPIController(BASE_URL, null, TEAM_ID)
|
|
386
|
+
const postData = PostsAPIController.generateRandomPostData()
|
|
387
|
+
|
|
388
|
+
noAuthAPI.createPost(postData).then((response: any) => {
|
|
389
|
+
expect(response.status).to.eq(401)
|
|
390
|
+
expect(response.body.success).to.be.false
|
|
391
|
+
|
|
392
|
+
cy.log('Creation without authentication rejected with 401')
|
|
393
|
+
})
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
it('POST_API_019: Should reject creation without x-team-id', () => {
|
|
397
|
+
const noTeamAPI = new PostsAPIController(BASE_URL, SUPERADMIN_API_KEY, null)
|
|
398
|
+
const postData = PostsAPIController.generateRandomPostData()
|
|
399
|
+
|
|
400
|
+
noTeamAPI.createPost(postData).then((response: any) => {
|
|
401
|
+
expect(response.status).to.eq(400)
|
|
402
|
+
expect(response.body.success).to.be.false
|
|
403
|
+
expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
|
|
404
|
+
|
|
405
|
+
cy.log('Creation without x-team-id rejected with TEAM_CONTEXT_REQUIRED')
|
|
406
|
+
})
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
// ============================================================
|
|
411
|
+
// GET /api/v1/posts/{id} - Get Post by ID
|
|
412
|
+
// ============================================================
|
|
413
|
+
describe('GET /api/v1/posts/{id} - Get Post by ID', () => {
|
|
414
|
+
let testPostId: string
|
|
415
|
+
|
|
416
|
+
beforeEach(() => {
|
|
417
|
+
// Create a test post
|
|
418
|
+
const postData = PostsAPIController.generateRandomPostData({
|
|
419
|
+
title: `Test Get By ID ${Date.now()}`
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
postsAPI.createPost(postData).then((response: any) => {
|
|
423
|
+
testPostId = response.body.data.id
|
|
424
|
+
createdPosts.push(testPostId)
|
|
425
|
+
})
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
it('POST_API_020: Should get post by valid ID', () => {
|
|
429
|
+
cy.then(() => {
|
|
430
|
+
postsAPI.getPostById(testPostId).then((response: any) => {
|
|
431
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
432
|
+
postsAPI.validatePostObject(response.body.data)
|
|
433
|
+
expect(response.body.data.id).to.eq(testPostId)
|
|
434
|
+
|
|
435
|
+
cy.log(`Got post: ${response.body.data.title}`)
|
|
436
|
+
})
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
it('POST_API_021: Should return 404 for non-existent post', () => {
|
|
441
|
+
postsAPI.getPostById('00000000-0000-0000-0000-000000000000').then((response: any) => {
|
|
442
|
+
postsAPI.validateErrorResponse(response, 404)
|
|
443
|
+
|
|
444
|
+
cy.log('Non-existent post returns 404')
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
it('POST_API_022: Should return 404 for invalid UUID format', () => {
|
|
449
|
+
postsAPI.getPostById('invalid-uuid-format').then((response: any) => {
|
|
450
|
+
postsAPI.validateErrorResponse(response, 404)
|
|
451
|
+
|
|
452
|
+
cy.log('Invalid UUID format returns 404')
|
|
453
|
+
})
|
|
454
|
+
})
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
// ============================================================
|
|
458
|
+
// PUT /api/v1/posts/{id} - Update Post
|
|
459
|
+
// ============================================================
|
|
460
|
+
describe('PUT /api/v1/posts/{id} - Update Post', () => {
|
|
461
|
+
let testPostId: string
|
|
462
|
+
|
|
463
|
+
beforeEach(() => {
|
|
464
|
+
const postData = PostsAPIController.generateRandomPostData({
|
|
465
|
+
title: `Test Update ${Date.now()}`
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
postsAPI.createPost(postData).then((response: any) => {
|
|
469
|
+
testPostId = response.body.data.id
|
|
470
|
+
createdPosts.push(testPostId)
|
|
471
|
+
})
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
it('POST_API_030: Should update post title', () => {
|
|
475
|
+
const updateData = {
|
|
476
|
+
title: 'Updated Title'
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
cy.then(() => {
|
|
480
|
+
postsAPI.updatePost(testPostId, updateData).then((response: any) => {
|
|
481
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
482
|
+
expect(response.body.data.title).to.eq(updateData.title)
|
|
483
|
+
|
|
484
|
+
cy.log(`Updated post title to: ${updateData.title}`)
|
|
485
|
+
})
|
|
486
|
+
})
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
it('POST_API_031: Should update post slug', () => {
|
|
490
|
+
const updateData = {
|
|
491
|
+
slug: `updated-slug-${Date.now()}`
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
cy.then(() => {
|
|
495
|
+
postsAPI.updatePost(testPostId, updateData).then((response: any) => {
|
|
496
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
497
|
+
expect(response.body.data.slug).to.eq(updateData.slug)
|
|
498
|
+
|
|
499
|
+
cy.log(`Updated post slug to: ${updateData.slug}`)
|
|
500
|
+
})
|
|
501
|
+
})
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
it('POST_API_032: Should update post excerpt and featured image', () => {
|
|
505
|
+
const updateData = {
|
|
506
|
+
excerpt: 'Updated excerpt text',
|
|
507
|
+
featuredImage: '/images/updated-featured.jpg'
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
cy.then(() => {
|
|
511
|
+
postsAPI.updatePost(testPostId, updateData).then((response: any) => {
|
|
512
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
513
|
+
expect(response.body.data.excerpt).to.eq(updateData.excerpt)
|
|
514
|
+
expect(response.body.data.featuredImage).to.eq(updateData.featuredImage)
|
|
515
|
+
|
|
516
|
+
cy.log('Updated excerpt and featured image')
|
|
517
|
+
})
|
|
518
|
+
})
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
it('POST_API_033: Should update post blocks', () => {
|
|
522
|
+
const updateData = {
|
|
523
|
+
blocks: [
|
|
524
|
+
{
|
|
525
|
+
id: `block-${Date.now()}`,
|
|
526
|
+
blockSlug: 'hero',
|
|
527
|
+
props: {
|
|
528
|
+
title: 'New Hero Block'
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
cy.then(() => {
|
|
535
|
+
postsAPI.updatePost(testPostId, updateData).then((response: any) => {
|
|
536
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
537
|
+
expect(response.body.data.blocks).to.have.length(1)
|
|
538
|
+
expect(response.body.data.blocks[0].blockSlug).to.eq('hero')
|
|
539
|
+
|
|
540
|
+
cy.log('Updated post blocks')
|
|
541
|
+
})
|
|
542
|
+
})
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
it('POST_API_034: Should publish post', () => {
|
|
546
|
+
const updateData = {
|
|
547
|
+
published: true
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
cy.then(() => {
|
|
551
|
+
postsAPI.updatePost(testPostId, updateData).then((response: any) => {
|
|
552
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
553
|
+
expect(response.body.data.published).to.be.true
|
|
554
|
+
|
|
555
|
+
cy.log('Post published successfully')
|
|
556
|
+
})
|
|
557
|
+
})
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
it('POST_API_035: Should update SEO fields', () => {
|
|
561
|
+
const updateData = {
|
|
562
|
+
seoTitle: 'Updated SEO Title',
|
|
563
|
+
seoDescription: 'Updated SEO Description',
|
|
564
|
+
noindex: true
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
cy.then(() => {
|
|
568
|
+
postsAPI.updatePost(testPostId, updateData).then((response: any) => {
|
|
569
|
+
postsAPI.validateSuccessResponse(response, 200)
|
|
570
|
+
expect(response.body.data.seoTitle).to.eq(updateData.seoTitle)
|
|
571
|
+
expect(response.body.data.seoDescription).to.eq(updateData.seoDescription)
|
|
572
|
+
expect(response.body.data.noindex).to.be.true
|
|
573
|
+
|
|
574
|
+
cy.log('Updated SEO fields')
|
|
575
|
+
})
|
|
576
|
+
})
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
it('POST_API_036: Should return 404 for non-existent post', () => {
|
|
580
|
+
postsAPI.updatePost('00000000-0000-0000-0000-000000000000', {
|
|
581
|
+
title: 'New Title'
|
|
582
|
+
}).then((response: any) => {
|
|
583
|
+
postsAPI.validateErrorResponse(response, 404)
|
|
584
|
+
|
|
585
|
+
cy.log('Update non-existent post returns 404')
|
|
586
|
+
})
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
it('POST_API_037: Should reject update without authentication', { tags: '@security' }, () => {
|
|
590
|
+
const noAuthAPI = new PostsAPIController(BASE_URL, null, TEAM_ID)
|
|
591
|
+
|
|
592
|
+
cy.then(() => {
|
|
593
|
+
noAuthAPI.updatePost(testPostId, { title: 'Unauthorized Update' }).then((response: any) => {
|
|
594
|
+
expect(response.status).to.eq(401)
|
|
595
|
+
expect(response.body.success).to.be.false
|
|
596
|
+
|
|
597
|
+
cy.log('Update without authentication rejected with 401')
|
|
598
|
+
})
|
|
599
|
+
})
|
|
600
|
+
})
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
// ============================================================
|
|
604
|
+
// DELETE /api/v1/posts/{id} - Delete Post
|
|
605
|
+
// ============================================================
|
|
606
|
+
describe('DELETE /api/v1/posts/{id} - Delete Post', () => {
|
|
607
|
+
it('POST_API_040: Should delete post by valid ID', () => {
|
|
608
|
+
// Create post to delete
|
|
609
|
+
const postData = PostsAPIController.generateRandomPostData({
|
|
610
|
+
title: `Test Delete ${Date.now()}`
|
|
611
|
+
})
|
|
612
|
+
|
|
613
|
+
postsAPI.createPost(postData).then((createResponse: any) => {
|
|
614
|
+
const postId = createResponse.body.data.id
|
|
615
|
+
|
|
616
|
+
// Delete the post
|
|
617
|
+
postsAPI.deletePost(postId).then((deleteResponse: any) => {
|
|
618
|
+
postsAPI.validateSuccessResponse(deleteResponse, 200)
|
|
619
|
+
|
|
620
|
+
// Verify deletion
|
|
621
|
+
postsAPI.getPostById(postId).then((getResponse: any) => {
|
|
622
|
+
expect(getResponse.status).to.eq(404)
|
|
623
|
+
cy.log(`Deleted and verified: ${postId}`)
|
|
624
|
+
})
|
|
625
|
+
})
|
|
626
|
+
})
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
it('POST_API_041: Should return 404 for non-existent post', () => {
|
|
630
|
+
postsAPI.deletePost('00000000-0000-0000-0000-000000000000').then((response: any) => {
|
|
631
|
+
postsAPI.validateErrorResponse(response, 404)
|
|
632
|
+
|
|
633
|
+
cy.log('Delete non-existent post returns 404')
|
|
634
|
+
})
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
it('POST_API_042: Should reject deletion without authentication', { tags: '@security' }, () => {
|
|
638
|
+
// Create a post first
|
|
639
|
+
const postData = PostsAPIController.generateRandomPostData()
|
|
640
|
+
|
|
641
|
+
postsAPI.createPost(postData).then((createResponse: any) => {
|
|
642
|
+
const postId = createResponse.body.data.id
|
|
643
|
+
createdPosts.push(postId)
|
|
644
|
+
|
|
645
|
+
// Try to delete without auth
|
|
646
|
+
const noAuthAPI = new PostsAPIController(BASE_URL, null, TEAM_ID)
|
|
647
|
+
noAuthAPI.deletePost(postId).then((response: any) => {
|
|
648
|
+
expect(response.status).to.eq(401)
|
|
649
|
+
expect(response.body.success).to.be.false
|
|
650
|
+
|
|
651
|
+
cy.log('Deletion without authentication rejected with 401')
|
|
652
|
+
})
|
|
653
|
+
})
|
|
654
|
+
})
|
|
655
|
+
})
|
|
656
|
+
|
|
657
|
+
// ============================================================
|
|
658
|
+
// Integration Test - Complete CRUD Lifecycle
|
|
659
|
+
// ============================================================
|
|
660
|
+
describe('Integration - Complete CRUD Lifecycle', () => {
|
|
661
|
+
it('POST_API_100: Should complete full lifecycle: Create -> Read -> Update -> Delete', () => {
|
|
662
|
+
const postData = PostsAPIController.generateRandomPostData({
|
|
663
|
+
title: 'Lifecycle Test Post',
|
|
664
|
+
excerpt: 'Lifecycle test excerpt'
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
// 1. CREATE
|
|
668
|
+
postsAPI.createPost(postData).then((createResponse: any) => {
|
|
669
|
+
postsAPI.validateSuccessResponse(createResponse, 201)
|
|
670
|
+
const createdPost = createResponse.body.data
|
|
671
|
+
cy.log(`1. Created post: ${createdPost.id}`)
|
|
672
|
+
|
|
673
|
+
// 2. READ
|
|
674
|
+
postsAPI.getPostById(createdPost.id).then((readResponse: any) => {
|
|
675
|
+
postsAPI.validateSuccessResponse(readResponse, 200)
|
|
676
|
+
expect(readResponse.body.data.title).to.eq(postData.title)
|
|
677
|
+
cy.log(`2. Read post: ${createdPost.id}`)
|
|
678
|
+
|
|
679
|
+
// 3. UPDATE
|
|
680
|
+
const updateData = {
|
|
681
|
+
title: 'Updated Lifecycle Post',
|
|
682
|
+
published: true,
|
|
683
|
+
excerpt: 'Updated excerpt'
|
|
684
|
+
}
|
|
685
|
+
postsAPI.updatePost(createdPost.id, updateData).then((updateResponse: any) => {
|
|
686
|
+
postsAPI.validateSuccessResponse(updateResponse, 200)
|
|
687
|
+
expect(updateResponse.body.data.title).to.eq(updateData.title)
|
|
688
|
+
expect(updateResponse.body.data.published).to.be.true
|
|
689
|
+
expect(updateResponse.body.data.excerpt).to.eq(updateData.excerpt)
|
|
690
|
+
cy.log(`3. Updated post: ${updateData.title}`)
|
|
691
|
+
|
|
692
|
+
// 4. DELETE
|
|
693
|
+
postsAPI.deletePost(createdPost.id).then((deleteResponse: any) => {
|
|
694
|
+
postsAPI.validateSuccessResponse(deleteResponse, 200)
|
|
695
|
+
cy.log(`4. Deleted post: ${createdPost.id}`)
|
|
696
|
+
|
|
697
|
+
// 5. VERIFY DELETION
|
|
698
|
+
postsAPI.getPostById(createdPost.id).then((finalResponse: any) => {
|
|
699
|
+
expect(finalResponse.status).to.eq(404)
|
|
700
|
+
cy.log('5. Verified deletion: post not found (404)')
|
|
701
|
+
cy.log('Full CRUD lifecycle completed successfully')
|
|
702
|
+
})
|
|
703
|
+
})
|
|
704
|
+
})
|
|
705
|
+
})
|
|
706
|
+
})
|
|
707
|
+
})
|
|
708
|
+
})
|
|
709
|
+
})
|