Canvas依存処理をテストする
9月15日の日報です。今日もなんかnode弄って終わりましたね…
Canvas依存処理をテストする
「クライアント側で画像をリサイズして、特定サイズのサムネイルを作りたい」という思いがあるとして、コードはこんな感じになると思います1。
なお「それはサーバー側でやれよ」みたいな話はなかったものとします。
export interface IImageSize { width: number; height: number; } export const resizeImage = ( base64: string, size: IImageSize, options = { createImage: Image, createCanvas: () => document.createElement("canvas") } ): Promise<string> => { // return result as promise return new Promise<string>((resolve, reject) => { // create canvas object const canvas = options.createCanvas() as HTMLCanvasElement; // create image object const image = new options.createImage(); // set event to resize image.onload = function() { // create required objects const context = canvas.getContext("2d"); if (!context) { return reject( new Error("resizeImage failed: canvas context is blank") ); } // set canvas size canvas.height = size.height; canvas.width = size.width; // resize context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); resolve(canvas.toDataURL()); }; image.onerror = function(error: ErrorEvent) { reject(error); }; // set image data image.src = base64; }); };
素晴らしい、Canvas
Image
ごり押しで無理やり変換できましたね! コピペ元はこちらで、最初こんな感じでしたがちょいちょい修正して終わり。
ただどう見てもブラウザー実装依存なので、node.jsでテストできなさそうな感じですが、引数 options
で色々誤魔化しているので大丈夫です(説明放棄)
import { IImageSize, resizeImage } from "~/store/uploader"; import fs from "fs"; import NodeCanvas from "canvas"; const getSampleBuffer = (): Buffer => { const path = `${__dirname}/test-neko.png`; return fs.readFileSync(path); }; describe("resizeImage", () => { let base64: string; let size: IImageSize; beforeEach(() => { // get base64 string from sample image file base64 = `data:image/png;base64,${getSampleBuffer().toString("base64")}`; // set image size // original: 70 x 125 size = { height: 56, width: 100 }; }); it("returns base64", async () => { const result = await resizeImage(base64, size, { createImage: NodeCanvas.Image, createCanvas: () => new NodeCanvas() }); expect(result).toMatch(/data:image\/png;.+/); }); it("returns resized data", async () => { const result = await resizeImage(base64, size, { createImage: NodeCanvas.Image, createCanvas: () => new NodeCanvas() }); console.log(result) expect(result.length).toBeLessThan(base64.length * 0.85); }); });
node-canvas
と依存ライブラリーのインストールが必要になるので、実はこのコードをコピペしただけでは動かないのですが2、公式ドキュメントを見れば多分なんとかなります。
テストの中身は別問題
これで一応テストは書けるんですが、実際にどういうテストを書くかというと… 正直すこしも思いつかなかったので「文字列としてちょっと短くなっているか」みたいな雑極まりないテスト1件で済ませています。
base64文字列ではなく image
を返して image.width
image.height
検証するくらいなら思いつきますね。ただ、ここで欲しいのはあくまでエンコードされた文字列なので、その変更はあんまりやりたくない感じです。
「見た感じちゃんとリサイズされてるじゃん!」というテストを書くのは(私には)無理そうなので、まあ当面これでオッケーということにします。解決。