Skip to content

Commit 724a6c0

Browse files
AminMemarianiclaude
andcommitted
fix CI: resolve editor type errors, fix test discovery, and field validation bug
- Make registerEditorBlock generic to fix BlockEditProps type variance errors - Add @types/node to editor package for process global - Fix BlockData | undefined assertion in editor-state splice - Replace --dir with positional path filter in CI vitest commands - Split test helpers from DB-dependent setup so unit tests skip PrismaClient - Remove global setupFiles from vitest config - Reject empty strings for required TEXT fields in field-validator Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cc5cb45 commit 724a6c0

10 files changed

Lines changed: 73 additions & 67 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@ jobs:
5656
- name: Generate Prisma client
5757
run: pnpm --filter @nextpress/db exec prisma generate
5858
- name: Run unit tests with coverage
59-
run: pnpm --filter @nextpress/core exec vitest run --reporter=verbose --coverage --dir src/__tests__/unit
60-
env:
61-
SKIP_DB: "true"
59+
run: pnpm --filter @nextpress/core exec vitest run --reporter=verbose --coverage src/__tests__/unit
6260
continue-on-error: true
6361
- name: Upload coverage to Codecov
6462
if: always()
@@ -107,7 +105,7 @@ jobs:
107105
- name: Push schema to test database
108106
run: pnpm --filter @nextpress/db exec prisma db push --skip-generate
109107
- name: Run integration tests
110-
run: pnpm --filter @nextpress/core exec vitest run --reporter=verbose --dir src/__tests__/integration
108+
run: pnpm --filter @nextpress/core exec vitest run --reporter=verbose src/__tests__/integration
111109
continue-on-error: true
112110

113111
# ── Gate ──
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Test helpers — pure utilities that do NOT require a database connection.
3+
* Use this in unit tests. Integration tests should import from "./setup".
4+
*/
5+
6+
import type { AuthContext, PermissionSlug } from "../auth/auth-types";
7+
8+
/** Create a mock AuthContext for testing */
9+
export function mockAuth(overrides: Partial<AuthContext> = {}): AuthContext {
10+
return {
11+
user: {
12+
id: "test-user-id",
13+
email: "test@example.com",
14+
name: "Test User",
15+
displayName: "Test User",
16+
image: null,
17+
},
18+
siteId: "test-site-id",
19+
role: "admin",
20+
permissions: new Set<PermissionSlug>([
21+
"create_content", "edit_own_content", "edit_others_content",
22+
"delete_own_content", "delete_others_content", "publish_content",
23+
"manage_categories", "manage_tags", "upload_media", "delete_media",
24+
"moderate_comments", "list_users", "create_users", "edit_users",
25+
"delete_users", "promote_users", "switch_themes", "customize_theme",
26+
"manage_menus", "activate_plugins", "manage_plugins", "manage_settings",
27+
"manage_content_types", "manage_fields", "manage_taxonomies",
28+
"manage_sites", "read", "edit_profile",
29+
]),
30+
...overrides,
31+
};
32+
}
33+
34+
/** Create a contributor auth (limited permissions) */
35+
export function mockContributorAuth(): AuthContext {
36+
return mockAuth({
37+
role: "contributor",
38+
permissions: new Set<PermissionSlug>([
39+
"create_content", "edit_own_content", "delete_own_content",
40+
"read", "edit_profile",
41+
]),
42+
});
43+
}
44+
45+
/** Create an editor auth */
46+
export function mockEditorAuth(): AuthContext {
47+
return mockAuth({
48+
role: "editor",
49+
permissions: new Set<PermissionSlug>([
50+
"create_content", "edit_own_content", "edit_others_content",
51+
"delete_own_content", "delete_others_content", "publish_content",
52+
"manage_categories", "manage_tags", "upload_media", "delete_media",
53+
"moderate_comments", "list_users", "manage_menus", "read", "edit_profile",
54+
]),
55+
});
56+
}

packages/core/src/__tests__/setup.ts

Lines changed: 4 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
* Provides:
55
* - Test database connection (uses TEST_DATABASE_URL)
66
* - Database cleanup between tests
7-
* - Mock auth context factory
8-
* - Common test fixtures
7+
* - Mock auth context factory (re-exported from helpers)
98
*/
109

1110
import { PrismaClient } from "@prisma/client";
12-
import type { AuthContext, PermissionSlug } from "../auth/auth-types";
11+
12+
// Re-export helpers so existing integration test imports keep working
13+
export { mockAuth, mockContributorAuth, mockEditorAuth } from "./helpers";
1314

1415
// ── Test database ──
1516

@@ -18,66 +19,13 @@ export const testPrisma = new PrismaClient({
1819
});
1920

2021
beforeAll(async () => {
21-
// Verify connection
2222
await testPrisma.$connect();
2323
});
2424

2525
afterAll(async () => {
2626
await testPrisma.$disconnect();
2727
});
2828

29-
// ── Test helpers ──
30-
31-
/** Create a mock AuthContext for testing */
32-
export function mockAuth(overrides: Partial<AuthContext> = {}): AuthContext {
33-
return {
34-
user: {
35-
id: "test-user-id",
36-
email: "test@example.com",
37-
name: "Test User",
38-
displayName: "Test User",
39-
image: null,
40-
},
41-
siteId: "test-site-id",
42-
role: "admin",
43-
permissions: new Set<PermissionSlug>([
44-
"create_content", "edit_own_content", "edit_others_content",
45-
"delete_own_content", "delete_others_content", "publish_content",
46-
"manage_categories", "manage_tags", "upload_media", "delete_media",
47-
"moderate_comments", "list_users", "create_users", "edit_users",
48-
"delete_users", "promote_users", "switch_themes", "customize_theme",
49-
"manage_menus", "activate_plugins", "manage_plugins", "manage_settings",
50-
"manage_content_types", "manage_fields", "manage_taxonomies",
51-
"manage_sites", "read", "edit_profile",
52-
]),
53-
...overrides,
54-
};
55-
}
56-
57-
/** Create a contributor auth (limited permissions) */
58-
export function mockContributorAuth(): AuthContext {
59-
return mockAuth({
60-
role: "contributor",
61-
permissions: new Set<PermissionSlug>([
62-
"create_content", "edit_own_content", "delete_own_content",
63-
"read", "edit_profile",
64-
]),
65-
});
66-
}
67-
68-
/** Create an editor auth */
69-
export function mockEditorAuth(): AuthContext {
70-
return mockAuth({
71-
role: "editor",
72-
permissions: new Set<PermissionSlug>([
73-
"create_content", "edit_own_content", "edit_others_content",
74-
"delete_own_content", "delete_others_content", "publish_content",
75-
"manage_categories", "manage_tags", "upload_media", "delete_media",
76-
"moderate_comments", "list_users", "manage_menus", "read", "edit_profile",
77-
]),
78-
});
79-
}
80-
8129
/** Clean up test data between tests */
8230
export async function cleanDatabase() {
8331
const tables = [

packages/core/src/__tests__/unit/permissions.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
canPublishContent,
1414
canAccessAdmin,
1515
} from "../../auth/permissions";
16-
import { mockAuth, mockContributorAuth, mockEditorAuth } from "../setup";
16+
import { mockAuth, mockContributorAuth, mockEditorAuth } from "../helpers";
1717

1818
describe("can()", () => {
1919
it("grants permission when role has it", () => {

packages/core/src/fields/field-validator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export function zodSchemaForField(def: FieldDefinition): z.ZodTypeAny {
2626
switch (def.fieldType) {
2727
case "TEXT": {
2828
let s = z.string();
29+
if (def.isRequired && typeof v.minLength !== "number") s = s.min(1);
2930
if (typeof v.minLength === "number") s = s.min(v.minLength);
3031
if (typeof v.maxLength === "number") s = s.max(v.maxLength);
3132
if (typeof v.pattern === "string") s = s.regex(new RegExp(v.pattern));

packages/core/vitest.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export default defineConfig({
55
test: {
66
globals: true,
77
environment: "node",
8-
setupFiles: ["./src/__tests__/setup.ts"],
98
include: ["src/__tests__/**/*.test.ts"],
109
coverage: {
1110
provider: "v8",

packages/editor/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"zod": "^3.23.0"
1212
},
1313
"devDependencies": {
14+
"@types/node": "^22.0.0",
1415
"@types/react": "^18.3.0",
1516
"typescript": "^5.5.0"
1617
},

packages/editor/src/core/block-registry.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@
1010
* is server-safe.
1111
*/
1212

13-
import type { ComponentType } from "react";
1413
import type { z } from "zod";
15-
import type { BlockEditProps } from "@nextpress/blocks";
1614
import type { EditorBlockDefinition } from "./types";
1715

1816
const editorRegistry = new Map<string, EditorBlockDefinition>();
1917

2018
/** Register a block's edit component for the editor */
21-
export function registerEditorBlock(def: EditorBlockDefinition): void {
22-
editorRegistry.set(def.type, def);
19+
export function registerEditorBlock<
20+
TSchema extends z.ZodObject<z.ZodRawShape>,
21+
>(def: EditorBlockDefinition<TSchema>): void {
22+
editorRegistry.set(def.type, def as EditorBlockDefinition);
2323
}
2424

2525
/** Get the editor definition (includes edit component) */

packages/editor/src/core/editor-state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export function editorReducer(
7474
const blockIndex = state.blocks.findIndex((b) => b.id === action.id);
7575
if (blockIndex === -1) return state;
7676
const newBlocks = [...state.blocks];
77-
const [moved] = newBlocks.splice(blockIndex, 1);
77+
const [moved] = newBlocks.splice(blockIndex, 1) as [BlockData];
7878
newBlocks.splice(action.toIndex, 0, moved);
7979
return pushHistory(state, {
8080
...state,

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)