diff options
Diffstat (limited to 'tools/image-thumbnails-test.js')
| -rw-r--r-- | tools/image-thumbnails-test.js | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/tools/image-thumbnails-test.js b/tools/image-thumbnails-test.js new file mode 100644 index 0000000..ede7e11 --- /dev/null +++ b/tools/image-thumbnails-test.js @@ -0,0 +1,55 @@ +// Real minified blob worker: both thumbnail sizes, animated originals, +// bounded concurrency and graceful fallback, with no page-thread encoding. +import assert from 'node:assert/strict'; +import puppeteer from './puppeteer.js'; +const bundle = await Bun.build({ entrypoints: ['src/image-thumbnails.js'], target: 'browser', minify: true }); +assert(bundle.success); +const js = await bundle.outputs[0].text(); +const server = Bun.serve({ port: 0, fetch: r => new URL(r.url).pathname === '/worker-client.js' + ? new Response(js, { headers: { 'Content-Type': 'text/javascript' } }) + : new Response('<script type="module">import {makeImageThumbnails} from "/worker-client.js";window.thumbnails=makeImageThumbnails;</script>', { headers: { 'Content-Type': 'text/html' } }) }); +const browser = await puppeteer.launch({ executablePath: '/usr/bin/google-chrome', headless: true, args: ['--no-sandbox'] }); +try { + const page = await browser.newPage(); + await page.goto(server.url.href); await page.waitForFunction(() => window.thumbnails); + const result = await page.evaluate(async () => { + const canvas = new OffscreenCanvas(3000,2000), ctx=canvas.getContext('2d'); + ctx.fillStyle='red';ctx.fillRect(0,0,3000,2000);ctx.fillStyle='blue';ctx.fillRect(500,0,2000,2000); + const blob=await canvas.convertToBlob({type:'image/png'}); + const decode = createImageBitmap, encode = OffscreenCanvas.prototype.convertToBlob; + let mainCalls=0, pending=0, maxPending=0; + const RealWorker = Worker; + window.Worker = class extends RealWorker { + constructor(...args) { super(...args); this.addEventListener('message',()=>pending--); } + postMessage(...args) { pending++;maxPending=Math.max(maxPending,pending);super.postMessage(...args); } + }; + window.createImageBitmap = () => {mainCalls++;throw Error('main-thread decode');}; + OffscreenCanvas.prototype.convertToBlob = () => {mainCalls++;throw Error('main-thread encode');}; + const [big, tiles, bad, recovered] = await Promise.all([ + thumbnails(blob,{width:1100,tileSize:360}), thumbnails(blob,{tileSize:360}), + thumbnails(new Blob(['broken'],{type:'image/jpeg'}),{width:1100}), thumbnails(blob,{width:1100}), + ]); + const media = await decode(big.media), tile = await decode(big.tile); + const square = new OffscreenCanvas(360,360);square.getContext('2d').drawImage(tile,0,0); + const pixel=[...square.getContext('2d').getImageData(0,180,1,1).data]; + const smallCanvas=new OffscreenCanvas(32,20);smallCanvas.getContext('2d'); + const smallBlob=await encode.call(smallCanvas,{type:'image/png'}); + const small=await thumbnails(smallBlob,{width:1100,tileSize:360}); + const smallTile=await decode(small.tile); + const gif=new Blob([Uint8Array.from(atob('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'),c=>c.charCodeAt(0))],{type:'image/gif'}); + const animated=await thumbnails(gif,{width:1100,tileSize:360}); + const out={mainCalls,maxPending,big:[big.width,big.height],media:[media.width,media.height],tile:[tile.width,tile.height],pixel, + tileOnly:!tiles.media&&!!tiles.tile,bad,recovered:!!recovered.media,smallOriginal:small.media===null,smallTile:[smallTile.width,smallTile.height], + animated: animated && [animated.width,animated.height,animated.media,animated.tile]}; + media.close();tile.close();smallTile.close();return out; + }); + console.log(result); + assert.equal(result.mainCalls,0);assert.equal(result.maxPending,1); + assert.deepEqual(result.big,[3000,2000]);assert.deepEqual(result.media,[1100,733]);assert.deepEqual(result.tile,[360,360]); + assert(result.pixel[2]>200&&result.pixel[0]<30,'tile crops the center'); + assert(result.tileOnly&&result.recovered&&result.smallOriginal);assert.equal(result.bad,null); + assert.deepEqual(result.smallTile,[20,20]);assert.deepEqual(result.animated,[1,1,null,null]); + const fallback=await browser.newPage();await fallback.goto(server.url.href);await fallback.waitForFunction(()=>window.thumbnails); + assert.equal(await fallback.evaluate(async()=>{window.Worker=class {constructor(){throw Error('blocked');}};return thumbnails(new Blob(['x']),{width:1100});}),null); + console.log('✓ thumbnails stay off the page thread; sizes, crop, originals, queue, bad-image recovery and worker fallback pass'); +} finally { await browser.close();server.stop(true); } |
