同步工作区剩余变更,主要包括: - 营销页面组件与布局持续优化(about/news/services/solutions/team 等) - 详情页四层叙事组件、布局组件、UI 组件调整 - CMS 数据模型、API 路由、权限、工作流、站内通知、媒体管理扩展 - 新增/补充单元测试与 E2E 测试(cms-workflow.spec.ts 等) - ESLint 9 迁移、jest/tsconfig 配置更新、依赖调整 - 新增 ADR、CMS 评估文档、Release Review / Acceptance 报告 - 移除水墨装饰组件与大体积未使用字体文件
107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
import { describe, it, expect, beforeAll } from '@jest/globals';
|
|
import sharp from 'sharp';
|
|
import {
|
|
isImage,
|
|
getImageMetadata,
|
|
generateDerivatives,
|
|
THUMBNAIL_SIZE,
|
|
} from './image-processor';
|
|
|
|
describe('image-processor', () => {
|
|
let testPng: Buffer;
|
|
let testJpeg: Buffer;
|
|
const txtBuffer = Buffer.from('not an image');
|
|
|
|
beforeAll(async () => {
|
|
testPng = await sharp({
|
|
create: {
|
|
width: 800,
|
|
height: 600,
|
|
channels: 3,
|
|
background: { r: 255, g: 0, b: 0 },
|
|
},
|
|
})
|
|
.png()
|
|
.toBuffer();
|
|
|
|
testJpeg = await sharp({
|
|
create: {
|
|
width: 400,
|
|
height: 300,
|
|
channels: 3,
|
|
background: { r: 0, g: 0, b: 255 },
|
|
},
|
|
})
|
|
.jpeg()
|
|
.toBuffer();
|
|
});
|
|
|
|
describe('isImage', () => {
|
|
it('returns true for common image mime types', () => {
|
|
expect(isImage('image/png')).toBe(true);
|
|
expect(isImage('image/jpeg')).toBe(true);
|
|
expect(isImage('image/webp')).toBe(true);
|
|
expect(isImage('image/avif')).toBe(true);
|
|
expect(isImage('image/gif')).toBe(true);
|
|
});
|
|
|
|
it('returns false for non-image mime types', () => {
|
|
expect(isImage('application/pdf')).toBe(false);
|
|
expect(isImage('text/plain')).toBe(false);
|
|
expect(isImage('video/mp4')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('getImageMetadata', () => {
|
|
it('returns width and height for a valid image buffer', async () => {
|
|
const meta = await getImageMetadata(testPng);
|
|
expect(meta).toEqual({ width: 800, height: 600 });
|
|
});
|
|
|
|
it('returns null for non-image buffer', async () => {
|
|
const meta = await getImageMetadata(txtBuffer);
|
|
expect(meta).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('generateDerivatives', () => {
|
|
it('generates thumbnail, webp and avif for image/png', async () => {
|
|
const derivatives = await generateDerivatives(testPng, 'image/png');
|
|
|
|
expect(derivatives.thumbnail).toBeDefined();
|
|
expect(derivatives.webp).toBeDefined();
|
|
expect(derivatives.avif).toBeDefined();
|
|
|
|
expect(derivatives.thumbnail!.width).toBeLessThanOrEqual(THUMBNAIL_SIZE);
|
|
expect(derivatives.thumbnail!.height).toBeLessThanOrEqual(THUMBNAIL_SIZE);
|
|
expect(derivatives.thumbnail!.path).toMatch(/-thumbnail\./);
|
|
expect(derivatives.thumbnail!.url).toMatch(/-thumbnail\./);
|
|
|
|
expect(derivatives.webp!.path).toMatch(/\.webp$/);
|
|
expect(derivatives.webp!.url).toMatch(/\.webp$/);
|
|
|
|
expect(derivatives.avif!.path).toMatch(/\.avif$/);
|
|
expect(derivatives.avif!.url).toMatch(/\.avif$/);
|
|
});
|
|
|
|
it('keeps original aspect ratio for thumbnail', async () => {
|
|
const derivatives = await generateDerivatives(testPng, 'image/png');
|
|
const thumb = derivatives.thumbnail!;
|
|
expect(thumb.width / thumb.height).toBeCloseTo(800 / 600, 1);
|
|
});
|
|
|
|
it('returns empty derivatives for non-image files', async () => {
|
|
const derivatives = await generateDerivatives(txtBuffer, 'text/plain');
|
|
expect(Object.keys(derivatives)).toHaveLength(0);
|
|
});
|
|
|
|
it('generates webp/avif with same dimensions as original', async () => {
|
|
const derivatives = await generateDerivatives(testJpeg, 'image/jpeg');
|
|
expect(derivatives.webp!.width).toBe(400);
|
|
expect(derivatives.webp!.height).toBe(300);
|
|
expect(derivatives.avif!.width).toBe(400);
|
|
expect(derivatives.avif!.height).toBe(300);
|
|
});
|
|
});
|
|
});
|