107 lines
3.7 KiB
TypeScript
107 lines
3.7 KiB
TypeScript
import { promises as fs } from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import sharp from 'sharp'
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import type { ProcessOptions } from '../../src/shared/types'
|
|
import {
|
|
ImageProcessor,
|
|
processSingleImage,
|
|
readSofHeight,
|
|
} from './image-processor'
|
|
|
|
describe('image processing algorithm', () => {
|
|
let temporaryDirectory: string
|
|
let inputPath: string
|
|
|
|
beforeEach(async () => {
|
|
temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'genuine-qingquwang-'))
|
|
inputPath = path.join(temporaryDirectory, 'sample.jpg')
|
|
await sharp({
|
|
create: {
|
|
width: 18,
|
|
height: 12,
|
|
channels: 3,
|
|
background: { r: 23, g: 105, b: 224 },
|
|
},
|
|
}).jpeg({ quality: 90 }).toFile(inputPath)
|
|
})
|
|
|
|
afterEach(async () => {
|
|
await fs.rm(temporaryDirectory, { recursive: true, force: true })
|
|
})
|
|
|
|
it('writes a .file without EOI while restoring original SOF height', async () => {
|
|
const outputPath = await processSingleImage(inputPath, options('file', 0))
|
|
const output = await fs.readFile(outputPath)
|
|
|
|
expect(outputPath).toBe(path.join(temporaryDirectory, 'sample.file'))
|
|
expect(readSofHeight(output)).toBe(12)
|
|
expect(output.subarray(-2).equals(Buffer.from([0xff, 0xd9]))).toBe(false)
|
|
})
|
|
|
|
it('supports .file.jpg and appends the configured random byte count', async () => {
|
|
const baselinePath = await processSingleImage(inputPath, options('file', 0))
|
|
const baselineSize = (await fs.stat(baselinePath)).size
|
|
const outputPath = await processSingleImage(inputPath, options('file-jpg', 0.01))
|
|
const outputSize = (await fs.stat(outputPath)).size
|
|
|
|
expect(outputPath).toBe(path.join(temporaryDirectory, 'sample.file.jpg'))
|
|
expect(outputSize - baselineSize).toBe(Math.floor(0.01 * 1024 * 1024))
|
|
})
|
|
|
|
it('discovers and converts supported non-JPEG inputs into JPEG delivery files', async () => {
|
|
const source = sharp({
|
|
create: {
|
|
width: 18,
|
|
height: 12,
|
|
channels: 4,
|
|
background: { r: 23, g: 105, b: 224, alpha: 0.5 },
|
|
},
|
|
})
|
|
const inputs = [
|
|
path.join(temporaryDirectory, 'sample.png'),
|
|
path.join(temporaryDirectory, 'sample.webp'),
|
|
path.join(temporaryDirectory, 'sample.gif'),
|
|
path.join(temporaryDirectory, 'sample.tiff'),
|
|
path.join(temporaryDirectory, 'sample.avif'),
|
|
]
|
|
await Promise.all([
|
|
source.clone().png().toFile(inputs[0]),
|
|
source.clone().webp().toFile(inputs[1]),
|
|
source.clone().gif().toFile(inputs[2]),
|
|
source.clone().tiff().toFile(inputs[3]),
|
|
source.clone().avif().toFile(inputs[4]),
|
|
fs.writeFile(path.join(temporaryDirectory, 'sample.svg'), '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="12"><rect width="18" height="12" fill="#1769e0"/></svg>'),
|
|
fs.writeFile(path.join(temporaryDirectory, 'ignored.bmp'), 'not-supported'),
|
|
])
|
|
|
|
const processor = new ImageProcessor()
|
|
const inspected = await processor.inspect([temporaryDirectory])
|
|
const inspectedNames = inspected.map((item) => item.name)
|
|
expect(inspectedNames).toEqual([
|
|
'sample.avif',
|
|
'sample.gif',
|
|
'sample.jpg',
|
|
'sample.png',
|
|
'sample.svg',
|
|
'sample.tiff',
|
|
'sample.webp',
|
|
])
|
|
|
|
for (const input of [...inputs, path.join(temporaryDirectory, 'sample.svg')]) {
|
|
const outputPath = await processSingleImage(input, options('file', 0))
|
|
expect(readSofHeight(await fs.readFile(outputPath))).toBe(12)
|
|
}
|
|
})
|
|
|
|
function options(outputFormat: 'file' | 'file-jpg', junkSizeMb: number): ProcessOptions {
|
|
return {
|
|
paths: [inputPath],
|
|
useSourceDirectory: true,
|
|
outputFormat,
|
|
junkSizeMb,
|
|
}
|
|
}
|
|
})
|