@nextsparkjs/theme-default 0.1.0-beta.20 → 0.1.0-beta.22
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 +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/support/e2e.ts +89 -0
- package/tests/cypress.config.ts +165 -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
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit Tests - Post Categories Validation Schemas
|
|
3
|
+
*
|
|
4
|
+
* Tests Zod validation for post categories API:
|
|
5
|
+
* - createCategorySchema: Name, slug, parentId, icon, color
|
|
6
|
+
* - Slug auto-generation logic
|
|
7
|
+
* - Edge cases: Special characters, empty strings, hierarchical validation
|
|
8
|
+
*
|
|
9
|
+
* Focus on validation logic WITHOUT database calls.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { z } from 'zod'
|
|
13
|
+
|
|
14
|
+
// Copy schemas from API route to test in isolation
|
|
15
|
+
const createCategorySchema = z.object({
|
|
16
|
+
name: z.string().min(1).max(255),
|
|
17
|
+
slug: z.string().min(2).max(100).regex(/^[a-z0-9-]+$/, 'Slug must be lowercase alphanumeric with dashes').optional(),
|
|
18
|
+
description: z.string().optional(),
|
|
19
|
+
icon: z.string().optional(),
|
|
20
|
+
color: z.string().optional(),
|
|
21
|
+
parentId: z.string().optional(),
|
|
22
|
+
order: z.number().int().default(0),
|
|
23
|
+
isDefault: z.boolean().default(false)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Copy helper function from API route
|
|
27
|
+
function generateSlug(name: string): string {
|
|
28
|
+
return name
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.trim()
|
|
31
|
+
.replace(/[^a-z0-9\s-]/g, '') // Remove special chars
|
|
32
|
+
.replace(/\s+/g, '-') // Replace spaces with dashes
|
|
33
|
+
.replace(/-+/g, '-') // Replace multiple dashes with single
|
|
34
|
+
.replace(/^-|-$/g, '') // Remove leading/trailing dashes
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('Post Categories Validation', () => {
|
|
38
|
+
describe('createCategorySchema - Valid Data', () => {
|
|
39
|
+
it('should accept minimal valid category data', () => {
|
|
40
|
+
const validData = {
|
|
41
|
+
name: 'Technology'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const result = createCategorySchema.safeParse(validData)
|
|
45
|
+
|
|
46
|
+
expect(result.success).toBe(true)
|
|
47
|
+
if (result.success) {
|
|
48
|
+
expect(result.data.name).toBe('Technology')
|
|
49
|
+
expect(result.data.order).toBe(0) // Default value
|
|
50
|
+
expect(result.data.isDefault).toBe(false) // Default value
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should accept complete category data with all fields', () => {
|
|
55
|
+
const completeData = {
|
|
56
|
+
name: 'Web Development',
|
|
57
|
+
slug: 'web-development',
|
|
58
|
+
description: 'Posts about web development',
|
|
59
|
+
icon: 'code',
|
|
60
|
+
color: '#3B82F6',
|
|
61
|
+
parentId: 'parent-cat-id',
|
|
62
|
+
order: 5,
|
|
63
|
+
isDefault: true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const result = createCategorySchema.safeParse(completeData)
|
|
67
|
+
|
|
68
|
+
expect(result.success).toBe(true)
|
|
69
|
+
if (result.success) {
|
|
70
|
+
expect(result.data.name).toBe('Web Development')
|
|
71
|
+
expect(result.data.slug).toBe('web-development')
|
|
72
|
+
expect(result.data.description).toBe('Posts about web development')
|
|
73
|
+
expect(result.data.icon).toBe('code')
|
|
74
|
+
expect(result.data.color).toBe('#3B82F6')
|
|
75
|
+
expect(result.data.parentId).toBe('parent-cat-id')
|
|
76
|
+
expect(result.data.order).toBe(5)
|
|
77
|
+
expect(result.data.isDefault).toBe(true)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('should accept category without slug (for auto-generation)', () => {
|
|
82
|
+
const data = {
|
|
83
|
+
name: 'Test Category'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = createCategorySchema.safeParse(data)
|
|
87
|
+
|
|
88
|
+
expect(result.success).toBe(true)
|
|
89
|
+
if (result.success) {
|
|
90
|
+
expect(result.data.slug).toBeUndefined()
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe('createCategorySchema - Name Validation', () => {
|
|
96
|
+
it('should reject empty name', () => {
|
|
97
|
+
const data = {
|
|
98
|
+
name: ''
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = createCategorySchema.safeParse(data)
|
|
102
|
+
|
|
103
|
+
expect(result.success).toBe(false)
|
|
104
|
+
if (!result.success) {
|
|
105
|
+
expect(result.error.issues[0].path).toContain('name')
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should reject name longer than 255 characters', () => {
|
|
110
|
+
const data = {
|
|
111
|
+
name: 'a'.repeat(256)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const result = createCategorySchema.safeParse(data)
|
|
115
|
+
|
|
116
|
+
expect(result.success).toBe(false)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should accept name with exactly 255 characters', () => {
|
|
120
|
+
const data = {
|
|
121
|
+
name: 'a'.repeat(255)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const result = createCategorySchema.safeParse(data)
|
|
125
|
+
|
|
126
|
+
expect(result.success).toBe(true)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should accept name with special characters', () => {
|
|
130
|
+
const validNames = [
|
|
131
|
+
'Technology & Science',
|
|
132
|
+
'Front-end Development',
|
|
133
|
+
'Tips/Tricks',
|
|
134
|
+
'Best Practices (2024)',
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
validNames.forEach(name => {
|
|
138
|
+
const result = createCategorySchema.safeParse({ name })
|
|
139
|
+
expect(result.success).toBe(true)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
describe('createCategorySchema - Slug Validation', () => {
|
|
145
|
+
it('should reject slug with uppercase letters', () => {
|
|
146
|
+
const data = {
|
|
147
|
+
name: 'Test',
|
|
148
|
+
slug: 'Test-Category'
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const result = createCategorySchema.safeParse(data)
|
|
152
|
+
|
|
153
|
+
expect(result.success).toBe(false)
|
|
154
|
+
if (!result.success) {
|
|
155
|
+
expect(result.error.issues[0].message).toContain('lowercase')
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should reject slug with special characters', () => {
|
|
160
|
+
const invalidSlugs = [
|
|
161
|
+
'test_category',
|
|
162
|
+
'test.category',
|
|
163
|
+
'test category',
|
|
164
|
+
'test@category',
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
invalidSlugs.forEach(slug => {
|
|
168
|
+
const result = createCategorySchema.safeParse({ name: 'Test', slug })
|
|
169
|
+
expect(result.success).toBe(false)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should reject slug shorter than 2 characters', () => {
|
|
174
|
+
const data = {
|
|
175
|
+
name: 'Test',
|
|
176
|
+
slug: 'a'
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const result = createCategorySchema.safeParse(data)
|
|
180
|
+
|
|
181
|
+
expect(result.success).toBe(false)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should reject slug longer than 100 characters', () => {
|
|
185
|
+
const data = {
|
|
186
|
+
name: 'Test',
|
|
187
|
+
slug: 'a'.repeat(101)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const result = createCategorySchema.safeParse(data)
|
|
191
|
+
|
|
192
|
+
expect(result.success).toBe(false)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('should accept valid slugs', () => {
|
|
196
|
+
const validSlugs = [
|
|
197
|
+
'technology',
|
|
198
|
+
'web-development',
|
|
199
|
+
'tips-tricks-2024',
|
|
200
|
+
'category-123',
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
validSlugs.forEach(slug => {
|
|
204
|
+
const result = createCategorySchema.safeParse({ name: 'Test', slug })
|
|
205
|
+
expect(result.success).toBe(true)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe('createCategorySchema - Optional Fields', () => {
|
|
211
|
+
it('should accept missing description', () => {
|
|
212
|
+
const data = {
|
|
213
|
+
name: 'Test'
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const result = createCategorySchema.safeParse(data)
|
|
217
|
+
|
|
218
|
+
expect(result.success).toBe(true)
|
|
219
|
+
if (result.success) {
|
|
220
|
+
expect(result.data.description).toBeUndefined()
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('should accept missing icon', () => {
|
|
225
|
+
const data = {
|
|
226
|
+
name: 'Test'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const result = createCategorySchema.safeParse(data)
|
|
230
|
+
|
|
231
|
+
expect(result.success).toBe(true)
|
|
232
|
+
if (result.success) {
|
|
233
|
+
expect(result.data.icon).toBeUndefined()
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('should accept missing color', () => {
|
|
238
|
+
const data = {
|
|
239
|
+
name: 'Test'
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const result = createCategorySchema.safeParse(data)
|
|
243
|
+
|
|
244
|
+
expect(result.success).toBe(true)
|
|
245
|
+
if (result.success) {
|
|
246
|
+
expect(result.data.color).toBeUndefined()
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('should accept missing parentId', () => {
|
|
251
|
+
const data = {
|
|
252
|
+
name: 'Test'
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const result = createCategorySchema.safeParse(data)
|
|
256
|
+
|
|
257
|
+
expect(result.success).toBe(true)
|
|
258
|
+
if (result.success) {
|
|
259
|
+
expect(result.data.parentId).toBeUndefined()
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
describe('createCategorySchema - Order Validation', () => {
|
|
265
|
+
it('should default order to 0', () => {
|
|
266
|
+
const data = {
|
|
267
|
+
name: 'Test'
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const result = createCategorySchema.safeParse(data)
|
|
271
|
+
|
|
272
|
+
expect(result.success).toBe(true)
|
|
273
|
+
if (result.success) {
|
|
274
|
+
expect(result.data.order).toBe(0)
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('should accept positive order values', () => {
|
|
279
|
+
const data = {
|
|
280
|
+
name: 'Test',
|
|
281
|
+
order: 5
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const result = createCategorySchema.safeParse(data)
|
|
285
|
+
|
|
286
|
+
expect(result.success).toBe(true)
|
|
287
|
+
if (result.success) {
|
|
288
|
+
expect(result.data.order).toBe(5)
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('should accept negative order values', () => {
|
|
293
|
+
const data = {
|
|
294
|
+
name: 'Test',
|
|
295
|
+
order: -1
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const result = createCategorySchema.safeParse(data)
|
|
299
|
+
|
|
300
|
+
expect(result.success).toBe(true)
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('should reject non-integer order values', () => {
|
|
304
|
+
const data = {
|
|
305
|
+
name: 'Test',
|
|
306
|
+
order: 5.5
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const result = createCategorySchema.safeParse(data)
|
|
310
|
+
|
|
311
|
+
expect(result.success).toBe(false)
|
|
312
|
+
})
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
describe('Slug Generation Logic', () => {
|
|
317
|
+
describe('generateSlug - Basic Functionality', () => {
|
|
318
|
+
it('should convert name to lowercase', () => {
|
|
319
|
+
expect(generateSlug('Technology')).toBe('technology')
|
|
320
|
+
expect(generateSlug('WEB DEVELOPMENT')).toBe('web-development')
|
|
321
|
+
expect(generateSlug('MixedCase')).toBe('mixedcase')
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('should replace spaces with dashes', () => {
|
|
325
|
+
expect(generateSlug('Web Development')).toBe('web-development')
|
|
326
|
+
expect(generateSlug('Tips and Tricks')).toBe('tips-and-tricks')
|
|
327
|
+
expect(generateSlug('Multiple Spaces')).toBe('multiple-spaces')
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it('should trim leading and trailing whitespace', () => {
|
|
331
|
+
expect(generateSlug(' Technology ')).toBe('technology')
|
|
332
|
+
expect(generateSlug('\tWeb Development\n')).toBe('web-development')
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('should remove leading and trailing dashes', () => {
|
|
336
|
+
expect(generateSlug('-Technology-')).toBe('technology')
|
|
337
|
+
expect(generateSlug('--Web Development--')).toBe('web-development')
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
describe('generateSlug - Special Characters', () => {
|
|
342
|
+
it('should remove special characters', () => {
|
|
343
|
+
expect(generateSlug('Technology & Science')).toBe('technology-science')
|
|
344
|
+
expect(generateSlug('Front-end Development')).toBe('front-end-development')
|
|
345
|
+
expect(generateSlug('Tips/Tricks')).toBe('tipstricks')
|
|
346
|
+
expect(generateSlug('Category @ 2024')).toBe('category-2024')
|
|
347
|
+
expect(generateSlug('Test!@#$%Category')).toBe('testcategory')
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('should preserve numbers', () => {
|
|
351
|
+
expect(generateSlug('2024 Trends')).toBe('2024-trends')
|
|
352
|
+
expect(generateSlug('Top 10 Tips')).toBe('top-10-tips')
|
|
353
|
+
expect(generateSlug('Web 3.0')).toBe('web-30')
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it('should handle parentheses', () => {
|
|
357
|
+
expect(generateSlug('Best Practices (2024)')).toBe('best-practices-2024')
|
|
358
|
+
expect(generateSlug('(Old) Category')).toBe('old-category')
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
describe('generateSlug - Multiple Dashes', () => {
|
|
363
|
+
it('should replace multiple dashes with single dash', () => {
|
|
364
|
+
expect(generateSlug('Web--Development')).toBe('web-development')
|
|
365
|
+
expect(generateSlug('Test---Category')).toBe('test-category')
|
|
366
|
+
expect(generateSlug('Multiple Spaces')).toBe('multiple-spaces')
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('should handle combination of special chars and spaces', () => {
|
|
370
|
+
expect(generateSlug('Web & Mobile Development')).toBe('web-mobile-development')
|
|
371
|
+
expect(generateSlug('Tips / Tricks 2024')).toBe('tips-tricks-2024')
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
describe('generateSlug - Edge Cases', () => {
|
|
376
|
+
it('should handle empty string', () => {
|
|
377
|
+
expect(generateSlug('')).toBe('')
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
it('should handle only special characters', () => {
|
|
381
|
+
expect(generateSlug('!@#$%')).toBe('')
|
|
382
|
+
expect(generateSlug('---')).toBe('')
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
it('should handle only spaces', () => {
|
|
386
|
+
expect(generateSlug(' ')).toBe('')
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('should handle unicode characters', () => {
|
|
390
|
+
// Non-ASCII characters should be removed
|
|
391
|
+
expect(generateSlug('Café')).toBe('caf')
|
|
392
|
+
expect(generateSlug('Niño Category')).toBe('nio-category')
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('should handle very long names', () => {
|
|
396
|
+
const longName = 'This is a very long category name that should be converted to a slug properly'
|
|
397
|
+
const slug = generateSlug(longName)
|
|
398
|
+
|
|
399
|
+
expect(slug).toBe('this-is-a-very-long-category-name-that-should-be-converted-to-a-slug-properly')
|
|
400
|
+
expect(slug.startsWith('-')).toBe(false)
|
|
401
|
+
expect(slug.endsWith('-')).toBe(false)
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('should handle already valid slugs', () => {
|
|
405
|
+
expect(generateSlug('web-development')).toBe('web-development')
|
|
406
|
+
expect(generateSlug('technology')).toBe('technology')
|
|
407
|
+
expect(generateSlug('tips-123')).toBe('tips-123')
|
|
408
|
+
})
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
describe('generateSlug - Real World Examples', () => {
|
|
412
|
+
it('should generate correct slugs for common category names', () => {
|
|
413
|
+
const examples = [
|
|
414
|
+
{ name: 'Technology', expected: 'technology' },
|
|
415
|
+
{ name: 'Web Development', expected: 'web-development' },
|
|
416
|
+
{ name: 'Tips & Tricks', expected: 'tips-tricks' },
|
|
417
|
+
{ name: 'News (2024)', expected: 'news-2024' },
|
|
418
|
+
{ name: 'Front-end Development', expected: 'front-end-development' },
|
|
419
|
+
{ name: 'AI / Machine Learning', expected: 'ai-machine-learning' },
|
|
420
|
+
{ name: 'Product Reviews', expected: 'product-reviews' },
|
|
421
|
+
{ name: 'How-to Guides', expected: 'how-to-guides' },
|
|
422
|
+
]
|
|
423
|
+
|
|
424
|
+
examples.forEach(({ name, expected }) => {
|
|
425
|
+
expect(generateSlug(name)).toBe(expected)
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
})
|
|
429
|
+
})
|