@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,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit Tests - PostHeader Component
|
|
3
|
+
*
|
|
4
|
+
* Tests PostHeader component logic and rendering:
|
|
5
|
+
* - Renders title, excerpt, and metadata
|
|
6
|
+
* - Handles optional fields (featuredImage, excerpt, categories)
|
|
7
|
+
* - Displays category badges with colors
|
|
8
|
+
* - Formats dates correctly
|
|
9
|
+
* - data-cy attribute presence
|
|
10
|
+
*
|
|
11
|
+
* Focus on component behavior WITHOUT full E2E interaction.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import React from 'react'
|
|
15
|
+
import { render, screen } from '@testing-library/react'
|
|
16
|
+
import { PostHeader } from '@/themes/default/entities/posts/components/post-header'
|
|
17
|
+
|
|
18
|
+
describe('PostHeader Component', () => {
|
|
19
|
+
const mockPost = {
|
|
20
|
+
title: 'Test Post Title',
|
|
21
|
+
excerpt: 'This is a test excerpt for the post',
|
|
22
|
+
featuredImage: 'https://example.com/image.jpg',
|
|
23
|
+
createdAt: '2024-12-16T10:00:00.000Z',
|
|
24
|
+
categories: [
|
|
25
|
+
{ id: 'cat-1', name: 'Technology', color: '#3B82F6' },
|
|
26
|
+
{ id: 'cat-2', name: 'Tutorials', color: '#10B981' }
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe('Rendering - Complete Post', () => {
|
|
31
|
+
it('should render post title', () => {
|
|
32
|
+
render(<PostHeader post={mockPost} />)
|
|
33
|
+
|
|
34
|
+
const title = screen.getByText('Test Post Title')
|
|
35
|
+
expect(title).toBeInTheDocument()
|
|
36
|
+
expect(title).toHaveAttribute('data-cy', 'post-title')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should render post excerpt', () => {
|
|
40
|
+
render(<PostHeader post={mockPost} />)
|
|
41
|
+
|
|
42
|
+
const excerpt = screen.getByText('This is a test excerpt for the post')
|
|
43
|
+
expect(excerpt).toBeInTheDocument()
|
|
44
|
+
expect(excerpt).toHaveAttribute('data-cy', 'post-excerpt')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should render featured image', () => {
|
|
48
|
+
render(<PostHeader post={mockPost} />)
|
|
49
|
+
|
|
50
|
+
const image = screen.getByAltText('Test Post Title')
|
|
51
|
+
expect(image).toBeInTheDocument()
|
|
52
|
+
expect(image).toHaveAttribute('src', expect.stringContaining('image.jpg'))
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should render all categories', () => {
|
|
56
|
+
render(<PostHeader post={mockPost} />)
|
|
57
|
+
|
|
58
|
+
expect(screen.getByText('Technology')).toBeInTheDocument()
|
|
59
|
+
expect(screen.getByText('Tutorials')).toBeInTheDocument()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should render formatted date', () => {
|
|
63
|
+
render(<PostHeader post={mockPost} />)
|
|
64
|
+
|
|
65
|
+
// Date should be formatted as "December 16, 2024"
|
|
66
|
+
const dateElement = screen.getByText(/December 16, 2024/i)
|
|
67
|
+
expect(dateElement).toBeInTheDocument()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should have main container with data-cy', () => {
|
|
71
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
72
|
+
|
|
73
|
+
const header = container.querySelector('[data-cy="post-header"]')
|
|
74
|
+
expect(header).toBeInTheDocument()
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('Rendering - Optional Fields', () => {
|
|
79
|
+
it('should not render featured image when missing', () => {
|
|
80
|
+
const postWithoutImage = {
|
|
81
|
+
...mockPost,
|
|
82
|
+
featuredImage: undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const { container } = render(<PostHeader post={postWithoutImage} />)
|
|
86
|
+
|
|
87
|
+
const imageContainer = container.querySelector('[data-cy="post-featured-image-display"]')
|
|
88
|
+
expect(imageContainer).not.toBeInTheDocument()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should not render excerpt when missing', () => {
|
|
92
|
+
const postWithoutExcerpt = {
|
|
93
|
+
...mockPost,
|
|
94
|
+
excerpt: undefined
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
render(<PostHeader post={postWithoutExcerpt} />)
|
|
98
|
+
|
|
99
|
+
const excerpt = screen.queryByTestId('post-excerpt')
|
|
100
|
+
expect(excerpt).not.toBeInTheDocument()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should not render categories section when empty', () => {
|
|
104
|
+
const postWithoutCategories = {
|
|
105
|
+
...mockPost,
|
|
106
|
+
categories: []
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const { container } = render(<PostHeader post={postWithoutCategories} />)
|
|
110
|
+
|
|
111
|
+
const categoriesContainer = container.querySelector('[data-cy="post-categories-display"]')
|
|
112
|
+
expect(categoriesContainer).not.toBeInTheDocument()
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should not render categories section when undefined', () => {
|
|
116
|
+
const postWithoutCategories = {
|
|
117
|
+
...mockPost,
|
|
118
|
+
categories: undefined
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const { container } = render(<PostHeader post={postWithoutCategories} />)
|
|
122
|
+
|
|
123
|
+
const categoriesContainer = container.querySelector('[data-cy="post-categories-display"]')
|
|
124
|
+
expect(categoriesContainer).not.toBeInTheDocument()
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
describe('Category Badges', () => {
|
|
129
|
+
it('should render category badge with correct color', () => {
|
|
130
|
+
render(<PostHeader post={mockPost} />)
|
|
131
|
+
|
|
132
|
+
const techBadge = screen.getByText('Technology')
|
|
133
|
+
|
|
134
|
+
// Badge should have inline styles with the color
|
|
135
|
+
const parentBadge = techBadge.closest('[style]')
|
|
136
|
+
expect(parentBadge).toHaveStyle({
|
|
137
|
+
borderColor: '#3B82F6',
|
|
138
|
+
color: '#3B82F6'
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should render category badge with background color opacity', () => {
|
|
143
|
+
render(<PostHeader post={mockPost} />)
|
|
144
|
+
|
|
145
|
+
const techBadge = screen.getByText('Technology')
|
|
146
|
+
const parentBadge = techBadge.closest('[style]')
|
|
147
|
+
|
|
148
|
+
// backgroundColor should be color + 20 (hex opacity)
|
|
149
|
+
expect(parentBadge).toHaveStyle({
|
|
150
|
+
backgroundColor: '#3B82F620'
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('should handle categories without color', () => {
|
|
155
|
+
const postWithNoColorCategory = {
|
|
156
|
+
...mockPost,
|
|
157
|
+
categories: [
|
|
158
|
+
{ id: 'cat-1', name: 'No Color Category' }
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
render(<PostHeader post={postWithNoColorCategory} />)
|
|
163
|
+
|
|
164
|
+
const badge = screen.getByText('No Color Category')
|
|
165
|
+
expect(badge).toBeInTheDocument()
|
|
166
|
+
|
|
167
|
+
// Badge should still render without inline color styles
|
|
168
|
+
const parentBadge = badge.closest('[style]')
|
|
169
|
+
// If no color, styles will be undefined or not include custom colors
|
|
170
|
+
if (parentBadge) {
|
|
171
|
+
const style = parentBadge.getAttribute('style')
|
|
172
|
+
expect(style).not.toContain('borderColor')
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('should render multiple category badges', () => {
|
|
177
|
+
const postWithManyCategories = {
|
|
178
|
+
...mockPost,
|
|
179
|
+
categories: [
|
|
180
|
+
{ id: 'cat-1', name: 'Category 1', color: '#FF0000' },
|
|
181
|
+
{ id: 'cat-2', name: 'Category 2', color: '#00FF00' },
|
|
182
|
+
{ id: 'cat-3', name: 'Category 3', color: '#0000FF' }
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
render(<PostHeader post={postWithManyCategories} />)
|
|
187
|
+
|
|
188
|
+
expect(screen.getByText('Category 1')).toBeInTheDocument()
|
|
189
|
+
expect(screen.getByText('Category 2')).toBeInTheDocument()
|
|
190
|
+
expect(screen.getByText('Category 3')).toBeInTheDocument()
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe('Date Formatting', () => {
|
|
195
|
+
it('should format date correctly for different dates', () => {
|
|
196
|
+
const testDates = [
|
|
197
|
+
{ input: '2024-01-01T00:00:00.000Z', expected: 'January 1, 2024' },
|
|
198
|
+
{ input: '2024-06-15T12:30:00.000Z', expected: 'June 15, 2024' },
|
|
199
|
+
{ input: '2024-12-31T23:59:59.000Z', expected: 'December 31, 2024' }
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
testDates.forEach(({ input, expected }) => {
|
|
203
|
+
const post = { ...mockPost, createdAt: input }
|
|
204
|
+
const { unmount } = render(<PostHeader post={post} />)
|
|
205
|
+
|
|
206
|
+
const dateElement = screen.getByText(new RegExp(expected, 'i'))
|
|
207
|
+
expect(dateElement).toBeInTheDocument()
|
|
208
|
+
|
|
209
|
+
unmount()
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('should include dateTime attribute on time element', () => {
|
|
214
|
+
render(<PostHeader post={mockPost} />)
|
|
215
|
+
|
|
216
|
+
const timeElement = screen.getByText(/December 16, 2024/i).closest('time')
|
|
217
|
+
expect(timeElement).toHaveAttribute('dateTime', '2024-12-16T10:00:00.000Z')
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
describe('Data-cy Attributes', () => {
|
|
222
|
+
it('should have data-cy on post header container', () => {
|
|
223
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
224
|
+
|
|
225
|
+
const header = container.querySelector('[data-cy="post-header"]')
|
|
226
|
+
expect(header).toBeInTheDocument()
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('should have data-cy on featured image container', () => {
|
|
230
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
231
|
+
|
|
232
|
+
const imageContainer = container.querySelector('[data-cy="post-featured-image-display"]')
|
|
233
|
+
expect(imageContainer).toBeInTheDocument()
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('should have data-cy on categories display', () => {
|
|
237
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
238
|
+
|
|
239
|
+
const categoriesDisplay = container.querySelector('[data-cy="post-categories-display"]')
|
|
240
|
+
expect(categoriesDisplay).toBeInTheDocument()
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('should have data-cy on post title', () => {
|
|
244
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
245
|
+
|
|
246
|
+
const title = container.querySelector('[data-cy="post-title"]')
|
|
247
|
+
expect(title).toBeInTheDocument()
|
|
248
|
+
expect(title?.textContent).toBe('Test Post Title')
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('should have data-cy on post excerpt', () => {
|
|
252
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
253
|
+
|
|
254
|
+
const excerpt = container.querySelector('[data-cy="post-excerpt"]')
|
|
255
|
+
expect(excerpt).toBeInTheDocument()
|
|
256
|
+
expect(excerpt?.textContent).toBe('This is a test excerpt for the post')
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
describe('Edge Cases', () => {
|
|
261
|
+
it('should handle empty title', () => {
|
|
262
|
+
const postWithEmptyTitle = {
|
|
263
|
+
...mockPost,
|
|
264
|
+
title: ''
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const { container } = render(<PostHeader post={postWithEmptyTitle} />)
|
|
268
|
+
|
|
269
|
+
// Component should still render without crashing
|
|
270
|
+
const header = container.querySelector('[data-cy="post-header"]')
|
|
271
|
+
expect(header).toBeInTheDocument()
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should handle very long title', () => {
|
|
275
|
+
const longTitle = 'A'.repeat(500)
|
|
276
|
+
const postWithLongTitle = {
|
|
277
|
+
...mockPost,
|
|
278
|
+
title: longTitle
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
render(<PostHeader post={postWithLongTitle} />)
|
|
282
|
+
|
|
283
|
+
const title = screen.getByText(longTitle)
|
|
284
|
+
expect(title).toBeInTheDocument()
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('should handle very long excerpt', () => {
|
|
288
|
+
const longExcerpt = 'B'.repeat(1000)
|
|
289
|
+
const postWithLongExcerpt = {
|
|
290
|
+
...mockPost,
|
|
291
|
+
excerpt: longExcerpt
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
render(<PostHeader post={postWithLongExcerpt} />)
|
|
295
|
+
|
|
296
|
+
const excerpt = screen.getByText(longExcerpt)
|
|
297
|
+
expect(excerpt).toBeInTheDocument()
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('should throw error for invalid date', () => {
|
|
301
|
+
const postWithInvalidDate = {
|
|
302
|
+
...mockPost,
|
|
303
|
+
createdAt: 'invalid-date'
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Component throws RangeError when date is invalid (toISOString fails)
|
|
307
|
+
expect(() => render(<PostHeader post={postWithInvalidDate} />)).toThrow(RangeError)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('should handle categories with very long names', () => {
|
|
311
|
+
const postWithLongCategoryName = {
|
|
312
|
+
...mockPost,
|
|
313
|
+
categories: [
|
|
314
|
+
{ id: 'cat-1', name: 'C'.repeat(100), color: '#FF0000' }
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
render(<PostHeader post={postWithLongCategoryName} />)
|
|
319
|
+
|
|
320
|
+
const categoryBadge = screen.getByText('C'.repeat(100))
|
|
321
|
+
expect(categoryBadge).toBeInTheDocument()
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('should handle special characters in title', () => {
|
|
325
|
+
const postWithSpecialChars = {
|
|
326
|
+
...mockPost,
|
|
327
|
+
title: 'Test <Title> & "Quotes" & Special ©haracters'
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
render(<PostHeader post={postWithSpecialChars} />)
|
|
331
|
+
|
|
332
|
+
const title = screen.getByText(/Test <Title> & "Quotes" & Special ©haracters/)
|
|
333
|
+
expect(title).toBeInTheDocument()
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
it('should handle missing category id', () => {
|
|
337
|
+
const postWithInvalidCategory = {
|
|
338
|
+
...mockPost,
|
|
339
|
+
categories: [
|
|
340
|
+
{ id: '', name: 'Invalid Category', color: '#FF0000' }
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
render(<PostHeader post={postWithInvalidCategory} />)
|
|
345
|
+
|
|
346
|
+
// Should still render category name
|
|
347
|
+
expect(screen.getByText('Invalid Category')).toBeInTheDocument()
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
describe('Component Structure', () => {
|
|
352
|
+
it('should render in correct order: image -> categories -> title -> excerpt -> date', () => {
|
|
353
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
354
|
+
|
|
355
|
+
const elements = container.querySelectorAll('[data-cy]')
|
|
356
|
+
const order = Array.from(elements).map(el => el.getAttribute('data-cy'))
|
|
357
|
+
|
|
358
|
+
// Expected order based on component structure
|
|
359
|
+
expect(order).toContain('post-featured-image-display')
|
|
360
|
+
expect(order).toContain('post-categories-display')
|
|
361
|
+
expect(order).toContain('post-title')
|
|
362
|
+
expect(order).toContain('post-excerpt')
|
|
363
|
+
|
|
364
|
+
// Featured image should come before title
|
|
365
|
+
const imageIndex = order.indexOf('post-featured-image-display')
|
|
366
|
+
const titleIndex = order.indexOf('post-title')
|
|
367
|
+
expect(imageIndex).toBeLessThan(titleIndex)
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('should render separator (hr) at the bottom', () => {
|
|
371
|
+
const { container } = render(<PostHeader post={mockPost} />)
|
|
372
|
+
|
|
373
|
+
const separator = container.querySelector('hr')
|
|
374
|
+
expect(separator).toBeInTheDocument()
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
})
|