71 lines
3.5 KiB
TypeScript
71 lines
3.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import test from "node:test";
|
|
|
|
const component = readFileSync(
|
|
new URL("../src/components/admin/model-management.tsx", import.meta.url),
|
|
"utf8",
|
|
);
|
|
const globals = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
|
|
|
|
test("provider form keeps codes server-owned and API keys write-only", () => {
|
|
assert.doesNotMatch(component, /name="code"|code:\s*values\.code/);
|
|
assert.match(component, /代码预览(服务端生成)/);
|
|
assert.match(component, /<Input readOnly value=\{editingProvider\?\.code \?\? "保存后由服务端自动生成"\}/);
|
|
assert.match(component, /name="apiKey"[\s\S]*required: !editingProvider[\s\S]*<Input\.Password/);
|
|
assert.match(component, /\.\.\.\(apiKey \? \{ apiKey \} : \{\}\)/);
|
|
assert.match(component, /留空表示保留现有 API Key/);
|
|
assert.match(component, /anthropic: "Anthropic 官方"/);
|
|
});
|
|
|
|
test("model discovery stays searchable with a manual provider model fallback", () => {
|
|
assert.match(component, /adminRequestJson<DiscoveredModelsPayload>\("\/api\/admin\/models\/discover"/);
|
|
assert.match(component, /body: JSON\.stringify\(\{ providerId: selectedProviderId \}\)/);
|
|
assert.match(component, /获取模型列表/);
|
|
assert.match(component, /<Select[\s\S]*showSearch[\s\S]*optionFilterProp="label"[\s\S]*onSelect=\{selectDiscoveredModel\}/);
|
|
assert.match(component, /name="providerModel" label="供应商模型名(可手工输入)"/);
|
|
assert.match(component, /providerModel\.toLowerCase\(\)\.replace\(\/\[\^a-z0-9\]\+\/g, "-"\)/);
|
|
assert.match(component, /!values\.modelId/);
|
|
assert.match(component, /!values\.label/);
|
|
});
|
|
|
|
test("provider rows expose a direct add-model entry with the provider preselected", () => {
|
|
assert.match(component, /function openModel\(model\?: ModelVersion, providerId\?: string\)/);
|
|
assert.match(component, /providerId: providerId \?\? providers\[0\]\?\.id/);
|
|
assert.match(component, /onClick=\{\(\) => openModel\(undefined, item\.id\)\}>添加模型<\/Button>/);
|
|
assert.match(component, />新增模型<\/Button>/);
|
|
});
|
|
|
|
test("model management input numbers fill their columns", () => {
|
|
const inputNumbers = [...component.matchAll(/<InputNumber\b[^>]*>/g)].map(([source]) => source);
|
|
assert.ok(inputNumbers.length > 0);
|
|
for (const inputNumber of inputNumbers) {
|
|
assert.match(inputNumber, /style=\{\{ width: "100%" \}\}/);
|
|
}
|
|
});
|
|
|
|
|
|
test("model management renders localized labels without changing stored enum values", () => {
|
|
assert.match(component, /openai: "OpenAI 官方"/);
|
|
assert.match(component, /anthropic: "Anthropic 官方"/);
|
|
assert.match(component, /"openai-compatible": "OpenAI 兼容接口"/);
|
|
assert.match(component, /standard: "标准"/);
|
|
assert.match(component, /premium: "高级"/);
|
|
assert.match(component, /internal: "内部"/);
|
|
assert.match(component, /draft: "草稿"/);
|
|
assert.match(component, /published: "已发布"/);
|
|
assert.match(component, /retired: "已下线"/);
|
|
});
|
|
|
|
test("global native input sizing excludes Ant Design picker internals", () => {
|
|
assert.match(
|
|
globals,
|
|
/input:not\(\[class\^="ant-"\]\):not\(\[class\*=" ant-"\]\):not\(\.ant-picker input\), select[^\{]+\{ width: 100%; min-height: 44px; padding: 0 12px;/,
|
|
);
|
|
assert.match(
|
|
globals,
|
|
/input:not\(\[class\^="ant-"\]\):not\(\[class\*=" ant-"\]\):not\(\.ant-picker input\):disabled/,
|
|
);
|
|
assert.doesNotMatch(globals, /(?:^|\n)input, select \{ width: 100%; min-height: 44px; padding: 0 12px;/);
|
|
});
|