Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: testing setup for image shrink #520

Merged
merged 5 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14,175 changes: 2,553 additions & 11,622 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/image-shrink/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
LICENSE
src/test/snapshots
coverage
5 changes: 0 additions & 5 deletions packages/image-shrink/jest.config.js

This file was deleted.

15 changes: 11 additions & 4 deletions packages/image-shrink/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"scripts": {
"prepack": "cp ../../LICENSE ./LICENSE",
"clean": "rimraf dist",
"test": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js",
"test": "npx playwright install-deps chromium && vitest --run --coverage",
"test:watch": "npx playwright install-deps chromium && vitest",
"prebuild": "npm run clean",
"build": "npm run build:types && npm run build:compile",
"build:types": "dts-bundle-generator --project tsconfig.dts.json -o dist/index.d.ts src/index.ts",
Expand All @@ -43,7 +44,13 @@
"signature"
],
"devDependencies": {
"ts-node": "^10.8.1"
},
"dependencies": {}
"@imagemagick/magick-wasm": "^0.0.28",
"@types/content-type": "^1.1.8",
"@vitest/browser": "^1.2.2",
"@vitest/coverage-istanbul": "^1.3.0",
"playwright": "^1.41.2",
"raw-body": "^2.5.2",
"ts-node": "^10.8.1",
"vitest": "^1.2.2"
}
}
12 changes: 12 additions & 0 deletions packages/image-shrink/src/test/helpers/getImageAttributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { readMagickImage } from './readMagickImage'

export const getImageAttributes = async (inputBlob: Blob) => {
return readMagickImage(inputBlob, (image) => {
return image.attributeNames.reduce((acc, name) => {
return {
...acc,
[name]: image.getAttribute(name)
}
}, {})
})
}
10 changes: 10 additions & 0 deletions packages/image-shrink/src/test/helpers/loadImageAsBlob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const loadImageAsBlob = async (
moduleResolver: () => Promise<{ default: string }>
) => {
const imageUrl = await moduleResolver().then((module) => module.default)
const response = await fetch(imageUrl)
const buffer = await response.arrayBuffer()
return new Blob([buffer], {
type: response.headers.get('content-type') ?? 'application/octet-stream'
})
}
17 changes: 17 additions & 0 deletions packages/image-shrink/src/test/helpers/loadImageMagick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/// <reference types="vite/client" />
import {
ImageMagick,
Magick,
MagickFormat,
Quantum,
initializeImageMagick
} from '@imagemagick/magick-wasm'
// eslint-disable-next-line import/no-unresolved
import wasmUrl from '@imagemagick/magick-wasm/magick.wasm?url'

export const loadImageMagick = async () => {
const wasmBytes = await fetch(wasmUrl).then((res) => res.arrayBuffer())
await initializeImageMagick(wasmBytes)

return { Magick, MagickFormat, Quantum, ImageMagick }
}
15 changes: 15 additions & 0 deletions packages/image-shrink/src/test/helpers/readMagickImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { loadImageMagick } from './loadImageMagick'
import { type IMagickImage } from '@imagemagick/magick-wasm'

export const readMagickImage = async <T>(
inputBlob: Blob,
func: (image: IMagickImage) => T
): Promise<T> => {
const { ImageMagick } = await loadImageMagick()
const blobArray = new Uint8Array(await inputBlob.arrayBuffer())
return new Promise<T>((resolve) => {
ImageMagick.read(blobArray, (image) => {
resolve(func(image))
})
})
}
13 changes: 13 additions & 0 deletions packages/image-shrink/src/test/helpers/uploadImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { TaskContext } from 'vitest'

export const uploadImage = (blob: Blob, variant: string, ctx?: TaskContext) => {
const ext = blob.type.replace('image/', '')
let filename = `${variant}.${ext}`
if (ctx) {
filename = `${ctx.task.suite.name}__${ctx.task.name}__${variant}.${ext}`
}
return fetch(`/upload-image?filename=${filename}`, {
method: 'POST',
body: blob
})
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/image-shrink/src/test/samples/line.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 16 additions & 17 deletions packages/image-shrink/src/utils/IccProfile/getIccProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@ export const getIccProfile = async (blob: Blob) => {
const iccProfile: DataView[] = []
const { promiseReadJpegChunks, stack } = readJpegChunks()

return await promiseReadJpegChunks(blob)
.then(() => {
stack.forEach(({ marker, view }) => {
if (marker === 0xe2) {
if (
// check for "ICC_PROFILE\0"
view.getUint32(0) === 0x4943435f &&
view.getUint32(4) === 0x50524f46 &&
view.getUint32(8) === 0x494c4500
) {
iccProfile.push(view)
}
}
})
return iccProfile
})
.catch(() => iccProfile)
await promiseReadJpegChunks(blob)

stack.forEach(({ marker, view }) => {
if (marker === 0xe2) {
if (
// check for "ICC_PROFILE\0"
view.getUint32(0) === 0x4943435f &&
view.getUint32(4) === 0x50524f46 &&
view.getUint32(8) === 0x494c4500
) {
iccProfile.push(view)
}
}
})

return iccProfile
}
36 changes: 18 additions & 18 deletions packages/image-shrink/src/utils/exif/getExif.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ export const getExif = async (blob: Blob) => {
let exif: DataView | null = null

const { promiseReadJpegChunks, stack } = readJpegChunks()
return promiseReadJpegChunks(blob)
.then(() => {
stack.forEach(({ marker, view }) => {
if (!exif && marker === 0xe1) {
if (view.byteLength >= 14) {
if (
// check for "Exif\0"
view.getUint32(0) === 0x45786966 &&
view.getUint16(4) === 0
) {
exif = view
return
}
}

await promiseReadJpegChunks(blob)

stack.forEach(({ marker, view }) => {
if (!exif && marker === 0xe1) {
if (view.byteLength >= 14) {
if (
// check for "Exif\0"
view.getUint32(0) === 0x45786966 &&
view.getUint16(4) === 0
) {
exif = view
return
}
})
return exif
})
.catch(() => exif)
}
}
})

return exif
}
4 changes: 2 additions & 2 deletions packages/image-shrink/src/utils/image/JPEG/readJpegChunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type TChunk = {
view: DataView
}

// TODO: unwrap promises
export const readJpegChunks = () => {
const stack: TChunk[] = []
const promiseReadJpegChunks = (blob: Blob) =>
Expand Down Expand Up @@ -37,7 +38,6 @@ export const readJpegChunks = () => {
break
}
}

readNextChunk()
})

Expand All @@ -50,7 +50,7 @@ export const readJpegChunks = () => {
return
}

const marker = view?.getUint8(1)
const marker = view.getUint8(1)

if (marker === 0xda) {
resolve(true)
Expand Down
75 changes: 35 additions & 40 deletions packages/image-shrink/src/utils/image/JPEG/replaceJpegChunk.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,47 @@
import { readJpegChunks } from './readJpegChunks'

export const replaceJpegChunk = (
export const replaceJpegChunk = async (
blob: Blob,
marker: number,
chunks: ArrayBuffer[]
) => {
return new Promise<Blob>((resolve, reject) => {
{
const oldChunkPos: number[] = []
const oldChunkLength: number[] = []

const { promiseReadJpegChunks, stack } = readJpegChunks()

return promiseReadJpegChunks(blob)
.then(() => {
stack.forEach((chunk) => {
if (chunk.marker === marker) {
oldChunkPos.push(chunk.startPos)
return oldChunkLength.push(chunk.length)
}
})
})
.then(() => {
const newChunks: (ArrayBuffer | Blob)[] = [blob.slice(0, 2)]

for (const chunk of chunks) {
const intro = new DataView(new ArrayBuffer(4))
intro.setUint16(0, 0xff00 + marker)
intro.setUint16(2, chunk.byteLength + 2)
newChunks.push(intro.buffer)
newChunks.push(chunk)
}

let pos = 2
for (let i = 0; i < oldChunkPos.length; i++) {
if (oldChunkPos[i] > pos) {
newChunks.push(blob.slice(pos, oldChunkPos[i]))
}
pos = oldChunkPos[i] + oldChunkLength[i] + 4
}

newChunks.push(blob.slice(pos, blob.size))

resolve(
new Blob(newChunks, {
type: blob.type
})
)
})
.catch(() => reject(blob))
}).catch(() => blob)
await promiseReadJpegChunks(blob)

stack.forEach((chunk) => {
if (chunk.marker === marker) {
oldChunkPos.push(chunk.startPos)
return oldChunkLength.push(chunk.length)
}
})

const newChunks: (ArrayBuffer | Blob)[] = [blob.slice(0, 2)]

for (const chunk of chunks) {
const intro = new DataView(new ArrayBuffer(4))
intro.setUint16(0, 0xff00 + marker)
intro.setUint16(2, chunk.byteLength + 2)
newChunks.push(intro.buffer)
newChunks.push(chunk)
}

let pos = 2
for (let i = 0; i < oldChunkPos.length; i++) {
if (oldChunkPos[i] > pos) {
newChunks.push(blob.slice(pos, oldChunkPos[i]))
}
pos = oldChunkPos[i] + oldChunkLength[i] + 4
}

newChunks.push(blob.slice(pos, blob.size))

return new Blob(newChunks, {
type: blob.type
})
}
}
2 changes: 1 addition & 1 deletion packages/image-shrink/src/utils/image/imageLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const processImage = (
resolve(image)
})
image.addEventListener('error', () => {
reject(image)
reject(new Error('Failed to load image. Probably not an image.'))
})
}
})
Expand Down
Loading
Loading