2026-06-30 14:58:06 -04:00
|
|
|
import { test } from 'node:test';
|
|
|
|
|
import assert from 'node:assert/strict';
|
2026-10-17 20:57:33 -04:00
|
|
|
import {
|
|
|
|
|
formatFileSize,
|
|
|
|
|
hasTransparency,
|
|
|
|
|
isAnimatedWebp,
|
|
|
|
|
isCompressible,
|
|
|
|
|
} from './imageCompression';
|
2026-06-30 14:58:06 -04:00
|
|
|
|
|
|
|
|
test('formatFileSize formats bytes below 1KB as B', () => {
|
|
|
|
|
assert.equal(formatFileSize(0), '0 B');
|
|
|
|
|
assert.equal(formatFileSize(1), '1 B');
|
|
|
|
|
assert.equal(formatFileSize(512), '512 B');
|
|
|
|
|
assert.equal(formatFileSize(1023), '1023 B');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('formatFileSize formats values in the KB range', () => {
|
|
|
|
|
assert.equal(formatFileSize(1024), '1.0 KB');
|
|
|
|
|
assert.equal(formatFileSize(1536), '1.5 KB');
|
|
|
|
|
// 1024 * 1024 - 1 is still under the MB boundary
|
|
|
|
|
assert.equal(formatFileSize(1024 * 1024 - 1), '1024.0 KB');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('formatFileSize formats values in the MB range', () => {
|
|
|
|
|
assert.equal(formatFileSize(1024 * 1024), '1.0 MB');
|
|
|
|
|
assert.equal(formatFileSize(1024 * 1024 * 1.5), '1.5 MB');
|
|
|
|
|
assert.equal(formatFileSize(1024 * 1024 * 10), '10.0 MB');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('isCompressible accepts raster image types', () => {
|
|
|
|
|
assert.equal(isCompressible({ type: 'image/png' } as Blob), true);
|
|
|
|
|
assert.equal(isCompressible({ type: 'image/jpeg' } as Blob), true);
|
|
|
|
|
assert.equal(isCompressible({ type: 'image/webp' } as Blob), true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('isCompressible rejects svg, empty type, and non-images', () => {
|
|
|
|
|
assert.equal(isCompressible({ type: 'image/svg+xml' } as Blob), false);
|
2026-10-17 20:57:33 -04:00
|
|
|
// [Gitea #331] usually animated; a canvas keeps one frame
|
|
|
|
|
assert.equal(isCompressible({ type: 'image/gif' } as Blob), false);
|
|
|
|
|
assert.equal(isCompressible({ type: 'image/apng' } as Blob), false);
|
2026-06-30 14:58:06 -04:00
|
|
|
assert.equal(isCompressible({ type: '' } as Blob), false);
|
|
|
|
|
assert.equal(isCompressible({ type: 'application/pdf' } as Blob), false);
|
|
|
|
|
assert.equal(isCompressible({ type: 'text/plain' } as Blob), false);
|
|
|
|
|
assert.equal(isCompressible({ type: 'video/mp4' } as Blob), false);
|
|
|
|
|
});
|
2026-10-17 20:57:33 -04:00
|
|
|
|
|
|
|
|
const webpHeader = (chunk: string, flags = 0) => {
|
|
|
|
|
const b = new Uint8Array(32);
|
|
|
|
|
const put = (at: number, str: string) => b.set(new TextEncoder().encode(str), at);
|
|
|
|
|
put(0, 'RIFF');
|
|
|
|
|
put(8, 'WEBP');
|
|
|
|
|
put(12, chunk);
|
|
|
|
|
b[20] = flags;
|
|
|
|
|
return b;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
test('isAnimatedWebp: VP8X with the animation flag (#331)', () => {
|
|
|
|
|
assert.equal(isAnimatedWebp(webpHeader('VP8X', 0x02)), true);
|
|
|
|
|
assert.equal(isAnimatedWebp(webpHeader('VP8X', 0x10)), false, 'alpha flag only');
|
|
|
|
|
assert.equal(isAnimatedWebp(webpHeader('VP8 ')), false, 'simple lossy');
|
|
|
|
|
assert.equal(isAnimatedWebp(new Uint8Array(8)), false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('hasTransparency (#331)', () => {
|
|
|
|
|
assert.equal(hasTransparency(new Uint8ClampedArray([1, 2, 3, 255, 4, 5, 6, 255])), false);
|
|
|
|
|
assert.equal(hasTransparency(new Uint8ClampedArray([1, 2, 3, 255, 0, 0, 0, 0])), true);
|
|
|
|
|
});
|