Skip to content

Commit 6178c40

Browse files
authored
Merge pull request #222 from InvolutionHell/activity
feat: 添加zod作为脚本验证,添加unit test
2 parents 43def74 + 779e9a3 commit 6178c40

File tree

10 files changed

+741
-69
lines changed

10 files changed

+741
-69
lines changed

.github/workflows/content-check.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ on:
77
- "**/*.mdx"
88
- "source.config.ts"
99
- "app/docs/**"
10+
- "data/**"
11+
- "tests/**"
1012
- "lib/source.ts"
1113
- "mdx-components.tsx"
1214
- "package.json"
@@ -26,15 +28,15 @@ jobs:
2628
- uses: actions/checkout@v4
2729

2830
- uses: pnpm/action-setup@v4
29-
with:
30-
version: 10
3131

3232
- uses: actions/setup-node@v4
3333
with:
3434
node-version: 20
3535
cache: "pnpm"
3636

3737
- run: pnpm install --frozen-lockfile
38+
- name: Run tests
39+
run: pnpm test
3840
# Non-blocking image migration + lint (visibility only)
3941
- name: Migrate images next to MDX (check only)
4042
run: pnpm migrate:images || echo "[warn] migrate:images failed (non-blocking)"

.github/workflows/deploy.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ jobs:
1717
- uses: actions/checkout@v4
1818

1919
- uses: pnpm/action-setup@v4
20-
with:
21-
version: 10
2220

2321
- uses: actions/setup-node@v4
2422
with:

.github/workflows/sync-uuid.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ jobs:
3737
- uses: actions/checkout@v4
3838

3939
- uses: pnpm/action-setup@v4
40-
with:
41-
version: 9
4240

4341
- uses: actions/setup-node@v4
4442
with:

.husky/pre-commit

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ git add -A
77
# 2) 校验图片路径与命名(不合规则阻止提交)
88
pnpm lint:images || exit 1
99

10-
# 3) 其余按 lint-staged 处理(如 Prettier)
10+
# 3) 运行 Vitest,确保配置校验通过
11+
pnpm test || exit 1
12+
13+
# 4) 其余按 lint-staged 处理(如 Prettier)
1114
pnpm exec lint-staged

app/components/ActivityTicker.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ import Image from "next/image";
44
import Link from "next/link";
55
import { useCallback, useEffect, useMemo, useState } from "react";
66
import { ChevronLeft, ChevronRight } from "lucide-react";
7-
import eventsData from "@/data/event.json";
8-
import type { ActivityEvent, ActivityEventsConfig } from "@/app/types/event";
7+
import {
8+
activityEventsConfig,
9+
type ActivityEvent,
10+
type ActivityTickerSettings,
11+
} from "@/app/types/event";
912
import { cn } from "@/lib/utils";
1013

14+
const { events: rawEvents, settings } = activityEventsConfig;
15+
1116
const {
12-
events: rawEvents,
13-
settings: {
14-
maxItems: configuredMaxItems = 3,
15-
rotationIntervalMs: configuredRotationIntervalMs = 8000,
16-
},
17-
} = eventsData as ActivityEventsConfig;
17+
maxItems: configuredMaxItems = 3,
18+
rotationIntervalMs: configuredRotationIntervalMs = 8000,
19+
}: ActivityTickerSettings = settings;
1820

1921
// 默认配置,从data/event.json中读取配置
2022
const MAX_ITEMS = configuredMaxItems;

app/types/event.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,48 @@
1-
/**
2-
* @description: 活动横幅所需要的类型
3-
* @param name 活动名称
4-
* @param discord discord活动链接
5-
* @param playback 回放链接
6-
* @param coverUrl 封面地址
7-
* @param deprecated 是否已经结束
8-
*/
9-
export interface ActivityEvent {
1+
import { z } from "zod";
2+
import eventsJson from "@/data/event.json";
3+
4+
export const ActivityEventSchema = z.object({
105
/** 活动名称,用于轮播标题 */
11-
name: string;
6+
name: z.string().min(1, "name 不能为空"),
127
/** Discord 活动入口链接 */
13-
discord: string;
8+
discord: z.string().min(1, "discord 入口不能为空"),
149
/** 活动回放链接,deprecated 为 true 时展示 */
15-
playback?: string;
10+
playback: z.string().min(1, "playback 链接不能为空").optional(),
1611
/** 活动封面,可以是静态资源相对路径或完整 URL */
17-
coverUrl: string;
12+
coverUrl: z.string().min(1, "coverUrl 不能为空"),
1813
/** 是否为已结束活动,true 时展示 Playback 按钮 */
19-
deprecated: boolean;
20-
}
14+
deprecated: z.boolean(),
15+
});
2116

22-
/** 活动轮播可配置参数 */
23-
export interface ActivityTickerSettings {
17+
export const ActivityTickerSettingsSchema = z.object({
2418
/** 首屏最多展示的活动数量 */
25-
maxItems: number;
19+
maxItems: z.number().int().positive("maxItems 需要为正整数"),
2620
/** 自动轮播的间隔时间(毫秒) */
27-
rotationIntervalMs: number;
28-
}
21+
rotationIntervalMs: z
22+
.number()
23+
.int()
24+
.positive("rotationIntervalMs 需要为正整数"),
25+
});
26+
27+
export const ActivityEventsConfigSchema = z.object({
28+
settings: ActivityTickerSettingsSchema,
29+
events: z.array(ActivityEventSchema),
30+
});
2931

30-
/** event.json 的整体结构 */
31-
export interface ActivityEventsConfig {
32-
settings: ActivityTickerSettings;
33-
events: ActivityEvent[];
32+
type ActivityEvent = z.infer<typeof ActivityEventSchema>;
33+
type ActivityTickerSettings = z.infer<typeof ActivityTickerSettingsSchema>;
34+
type ActivityEventsConfig = z.infer<typeof ActivityEventsConfigSchema>;
35+
36+
const parsedEventsConfig = ActivityEventsConfigSchema.safeParse(eventsJson);
37+
38+
if (!parsedEventsConfig.success) {
39+
const issueMessages = parsedEventsConfig.error.issues
40+
.map((issue) => `- ${issue.path.join(".") || "(root)"}: ${issue.message}`)
41+
.join("\n");
42+
throw new Error(`event.json 配置不合法:\n${issueMessages}`);
3443
}
44+
45+
export const activityEventsConfig: ActivityEventsConfig =
46+
parsedEventsConfig.data;
47+
48+
export type { ActivityEvent, ActivityEventsConfig, ActivityTickerSettings };

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"prebuild": "node scripts/escape-angles.mjs",
88
"build": "next build",
99
"start": "next start -p 3000",
10+
"test": "vitest run",
11+
"test:watch": "vitest",
1012
"postinstall": "fumadocs-mdx",
1113
"prepare": "husky",
1214
"lint:images": "node scripts/check-images.mjs",
@@ -90,9 +92,11 @@
9092
"tw-animate-css": "^1.3.8",
9193
"typescript": "^5.9.2",
9294
"typescript-eslint": "^8.46.2",
93-
"vercel": "^48.1.0"
95+
"vercel": "^48.1.0",
96+
"vitest": "^4.0.6"
9497
},
9598
"lint-staged": {
9699
"**/*": "prettier --write --ignore-unknown"
97-
}
100+
},
101+
"packageManager": "pnpm@10.20.0"
98102
}

0 commit comments

Comments
 (0)