Files
novalon-website/src/lib/media/image-processor.test.ts
T
张翔 10404dbb36 chore: sync marketing pages, CMS extensions, tests and project docs
同步工作区剩余变更,主要包括:
- 营销页面组件与布局持续优化(about/news/services/solutions/team 等)
- 详情页四层叙事组件、布局组件、UI 组件调整
- CMS 数据模型、API 路由、权限、工作流、站内通知、媒体管理扩展
- 新增/补充单元测试与 E2E 测试(cms-workflow.spec.ts 等)
- ESLint 9 迁移、jest/tsconfig 配置更新、依赖调整
- 新增 ADR、CMS 评估文档、Release Review / Acceptance 报告
- 移除水墨装饰组件与大体积未使用字体文件
2026-07-25 08:04:01 +08:00

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);
});
});
});