import { describe, expect, test } from "bun:test"; import { renderToStaticMarkup } from "react-dom/server"; import { ImageSizePicker } from "../src/components/image-size-picker"; import { ImageSizePresetsEditor } from "../src/components/image-size-presets-editor"; import { ModelCapabilityEditor } from "../src/components/model-capability-editor"; import { defaultImageCapabilityConfig, normalizeModelCapabilityConfig } from "../src/lib/model-capabilities"; import { IMAGE_RATIOS, IMAGE_RESOLUTIONS, imagePresetForRatio, imagePresetValue, imageQualityForTier, imageSizeConfigWithPresets, imageSizePresets, imageTierAvailable } from "../src/lib/image-size-presets"; import { buildImageResolutionOptions } from "../src/lib/image-resolution-tiers"; import { resolveImageRequestSize, validateImageSize } from "../src/services/api/image-validation"; import { buildGeminiImageGenerationConfig } from "../src/lib/gemini-image"; describe("统一图片分辨率与宽高比", () => { test("像素协议从旧比例配置切换 4K 后发送真实像素,不回退到 1K", () => { const profile = defaultImageCapabilityConfig(); profile.size = { parameter: "size", values: ["16:9"], default: "16:9", allowCustom: true }; const value = imagePresetValue(profile, imagePresetForRatio("4k", "16:9")); expect(value).toBe("3840x2160"); expect(resolveImageRequestSize(profile, "auto", value)).toEqual({ parameter: "size", value: "3840x2160" }); const legacy = renderToStaticMarkup( {}} />); expect(legacy).toContain("1824 × 1024 px"); const reopened = renderToStaticMarkup( {}} />); expect(reopened).not.toMatch(/]*>4K/); expect(reopened).toContain("2:1 · 3840 × 1920 px"); }); test("常用比例保持明确尺寸,等价比例归一", () => { expect(imagePresetForRatio("1k", "16:9").size).toBe("1824x1024"); expect(imagePresetForRatio("4k", "9:16").size).toBe("2160x3840"); expect(imagePresetForRatio("1k", "32:18")).toEqual(imagePresetForRatio("1k", "16:9")); expect(imagePresetForRatio("2k", "7:3").ratio).toBe("21:9"); expect(imagePresetForRatio("2k", "34:22").ratio).toBe("17:11"); }); test("三档预设与任意合理比例都满足像素范围", () => { for (const tier of IMAGE_RESOLUTIONS) for (const ratio of [...IMAGE_RATIOS, "17:11", "3:1", "1:3", "99999:99998"]) { const preset = imagePresetForRatio(tier, ratio); expect(() => validateImageSize(preset.width, preset.height, false)).not.toThrow(); } for (const ratio of ["0:9", "4:1", "1:4", "NaN:1", "1.5:1", "999999999:1"]) { expect(() => imagePresetForRatio("1k", ratio)).toThrow(); } }); test("管理员设置同步支持值、默认值,并保持精确尺寸", () => { const profile = defaultImageCapabilityConfig(); profile.size = { parameter: "size", values: ["1920x1080"], default: "1920x1080", allowCustom: false }; const existing = imageSizePresets(profile); const added = imagePresetForRatio("1k", "17:11"); profile.size = imageSizeConfigWithPresets(profile, [...existing, added]); expect(profile.size.values).toEqual(["1920x1080", added.size]); expect(profile.size.default).toBe("1920x1080"); expect(resolveImageRequestSize(profile, undefined, "1920x1080")?.value).toBe("1920x1080"); expect(normalizeModelCapabilityConfig({ version: 1, image: profile }).image?.size.presets).toEqual(profile.size.presets); expect(imageSizeConfigWithPresets(profile, [added]).default).toBe(added.size); }); test("非标准精确像素不会从选项中消失", () => { expect(buildImageResolutionOptions(["1360x880"])[0]).toMatchObject({ size: "1360x880", ratio: "17:11" }); expect(buildImageResolutionOptions(["0x1024", "1024x0"])).toEqual([]); }); test("比例协议分别发送比例和分辨率,不把自定义权限当作 4K 能力", () => { const grok = defaultImageCapabilityConfig("grok-image", "grok-imagine-image"); grok.size.allowCustom = true; expect(imageTierAvailable(grok, "4k")).toBe(false); expect(imageSizePresets(grok).some((preset) => preset.tier === "4k")).toBe(false); const gemini = defaultImageCapabilityConfig("gemini-image", "test"); const preset = imagePresetForRatio("4k", "16:9"); expect(buildGeminiImageGenerationConfig(imagePresetValue(gemini, preset), imageQualityForTier(gemini, "4k")).imageConfig).toEqual({ aspectRatio: "16:9", imageSize: "4K" }); gemini.size.allowCustom = true; const reopened = renderToStaticMarkup( {}} />); expect(reopened).toMatch(/aria-pressed="true"[^>]*>4K/); expect(reopened).toContain("17:11"); }); test("比例与档位的组合必须在该档位配置中,允许自定义则放开比例", () => { const profile = defaultImageCapabilityConfig("gemini-image", "test"); profile.size = imageSizeConfigWithPresets(profile, [imagePresetForRatio("1k", "1:1"), imagePresetForRatio("2k", "16:9")]); expect(() => resolveImageRequestSize(profile, "medium", "1:1")).toThrow("当前分辨率"); expect(resolveImageRequestSize(profile, "medium", "16:9")).toEqual({ parameter: "aspect_ratio", value: "16:9" }); profile.size.allowCustom = true; expect(resolveImageRequestSize(profile, "medium", "1:1")?.value).toBe("1:1"); }); test("管理员固定显示三档,用户端隐藏未支持档位,不发送尺寸时隐藏", () => { const profile = defaultImageCapabilityConfig("grok-image", "grok-imagine-image"); profile.size.allowCustom = true; const editor = renderToStaticMarkup( {}} />); for (const label of ["1K", "2K", "4K"]) expect(editor).toMatch(new RegExp(`]*>${label}`)); const picker = renderToStaticMarkup( {}} />); expect(picker).not.toMatch(/]*>4K/); expect(picker).toContain("换算参考"); profile.size.parameter = "none"; expect(renderToStaticMarkup( {}} />)).toBe(""); }); test("空档位始终隐藏,允许自定义也不显示;旧尺寸不导致比例区域为空", () => { for (const protocol of ["openai-image", "gemini-image"]) { const profile = defaultImageCapabilityConfig(protocol, "test"); profile.size = imageSizeConfigWithPresets(profile, [imagePresetForRatio("1k", "1:1"), imagePresetForRatio("4k", "16:9")]); for (const allowCustom of [false, true]) { profile.size.allowCustom = allowCustom; const picker = renderToStaticMarkup( {}} />); expect(picker).toMatch(/]*>1K/); expect(picker).toMatch(/]*>4K/); expect(picker).not.toMatch(/]*>2K/); expect(picker).not.toContain("未配置比例"); expect(picker).toContain('title="1:1 · 1024 × 1024"'); expect(picker).not.toMatch(/aria-pressed="true"[^>]*>1K/); } } }); test("仅配置一档时只显示该档;全空时保留自动尺寸与自定义入口", () => { const profile = defaultImageCapabilityConfig(); profile.size = imageSizeConfigWithPresets(profile, [imagePresetForRatio("4k", "16:9")]); const picker = renderToStaticMarkup( {}} />); expect(picker).not.toMatch(/]*>[12]K/); expect(picker).toMatch(/aria-pressed="true"[^>]*>4K/); profile.size = imageSizeConfigWithPresets(profile, []); profile.size.allowCustom = true; const empty = renderToStaticMarkup( {}} />); expect(empty).not.toContain('class="image-size-tiers"'); expect(empty).not.toContain('class="image-size-label"'); expect(empty).toContain("自动尺寸"); expect(empty).toContain("自定义比例或尺寸"); }); test("管理员比例直接点选和输入,默认输出不再使用下拉框", () => { const profile = defaultImageCapabilityConfig(); profile.size = imageSizeConfigWithPresets(profile, [imagePresetForRatio("1k", "16:9")]); const editor = renderToStaticMarkup( {}} />); expect(editor).not.toContain('role="combobox"'); expect(editor).not.toContain("ant-select"); for (const tier of ["1K", "2K", "4K"]) { expect(editor).toContain(`aria-label="${tier} 自定义比例"`); expect(editor).toContain(`aria-label="添加 ${tier} 比例"`); expect(editor).toContain(`aria-label="${tier} 16:9"`); } expect(editor).toContain('aria-label="默认输出"'); const readOnly = renderToStaticMarkup( {}} />); const buttons = [...readOnly.matchAll(/]*>/g)]; expect(buttons.length).toBeGreaterThan(24); expect(buttons.every(([button]) => button.includes('disabled=""'))).toBe(true); }); test("图片协议尺寸独占整行且先于辅助参数,视频布局不受影响", () => { const image = renderToStaticMarkup(); expect(image).toContain("admin-image-protocol-grid"); expect(image).toContain("admin-image-size-card"); expect(image.indexOf("尺寸参数")).toBeLessThan(image.indexOf("输出数量")); const video = renderToStaticMarkup(); expect(video).not.toContain("admin-image-protocol-grid"); }); });