diff --git a/packages/astro/test/core-image-service.test.js b/packages/astro/test/core-image-service.test.js deleted file mode 100644 index 60b04a1762ef..000000000000 --- a/packages/astro/test/core-image-service.test.js +++ /dev/null @@ -1,204 +0,0 @@ -import assert from 'node:assert/strict'; -import { after, before, describe, it } from 'node:test'; -import { removeDir } from '@astrojs/internal-helpers/fs'; -import * as cheerio from 'cheerio'; -import { lookup as probe } from '../dist/assets/utils/vendor/image-size/lookup.js'; -import { loadFixture } from './test-utils.js'; - -async function getImageDimensionsFromFixture(fixture, path) { - /** @type { Response } */ - const res = await fixture.fetch(path instanceof URL ? path.pathname + path.search : path); - const buffer = await res.arrayBuffer(); - const { width, height } = await probe(new Uint8Array(buffer)); - return { width, height }; -} - -async function getImageDimensionsFromLocalFile(fixture, path) { - const buffer = await fixture.readFile(path, null); - const { width, height } = await probe(new Uint8Array(buffer)); - return { width, height }; -} - -describe('astro image service', () => { - /** @type {import('./test-utils').Fixture} */ - let fixture; - - describe('dev image service', () => { - /** @type {import('./test-utils').DevServer} */ - let devServer; - - before(async () => { - fixture = await loadFixture({ - root: './fixtures/core-image-layout/', - image: { - domains: ['unsplash.com'], - }, - }); - - devServer = await fixture.startDevServer({}); - }); - - after(async () => { - await devServer.stop(); - }); - - describe('generated images', () => { - let $; - let src; - before(async () => { - const res = await fixture.fetch('/fit'); - const html = await res.text(); - $ = cheerio.load(html); - let $img = $('#local-both img'); - src = new URL($img.attr('src'), 'http://localhost').href; - }); - - it('generates correct width and height when both are provided', async () => { - const url = new URL(src); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, 300); - assert.equal(height, 400); - }); - - it('generates correct height when only width is provided', async () => { - const url = new URL(src); - url.searchParams.delete('h'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, 300); - assert.equal(height, 200); - }); - - it('generates correct width when only height is provided', async () => { - const url = new URL(src); - url.searchParams.delete('w'); - url.searchParams.set('h', '400'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, 600); - assert.equal(height, 400); - }); - - it('preserves aspect ratio when fit=inside', async () => { - const url = new URL(src); - url.searchParams.set('fit', 'inside'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, 300); - assert.equal(height, 200); - }); - - it('preserves aspect ratio when fit=contain', async () => { - const url = new URL(src); - url.searchParams.set('fit', 'contain'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, 300); - assert.equal(height, 200); - }); - - it('preserves aspect ratio when fit=outside', async () => { - const url = new URL(src); - url.searchParams.set('fit', 'outside'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, 600); - assert.equal(height, 400); - }); - const originalWidth = 2316; - const originalHeight = 1544; - it('does not upscale image if requested size is larger than original', async () => { - const url = new URL(src); - url.searchParams.set('w', '3000'); - url.searchParams.set('h', '2000'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, originalWidth); - assert.equal(height, originalHeight); - }); - - it('does not upscale image if requested size is larger than original and fit is unset', async () => { - const url = new URL(src); - url.searchParams.set('w', '3000'); - url.searchParams.set('h', '2000'); - url.searchParams.delete('fit'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, originalWidth); - assert.equal(height, originalHeight); - }); - - it('does not upscale if only one dimension is provided and fit is set', async () => { - const url = new URL(src); - url.searchParams.set('w', '3000'); - url.searchParams.delete('h'); - url.searchParams.set('fit', 'cover'); - const { width, height } = await getImageDimensionsFromFixture(fixture, url); - assert.equal(width, originalWidth); - assert.equal(height, originalHeight); - }); - }); - }); - - describe('build image service', () => { - before(async () => { - fixture = await loadFixture({ - root: './fixtures/core-image-layout/', - }); - removeDir(new URL('./fixtures/core-image-ssg/node_modules/.astro', import.meta.url)); - - await fixture.build(); - }); - - describe('generated images', () => { - let $; - before(async () => { - const html = await fixture.readFile('/build/index.html'); - $ = cheerio.load(html); - }); - - it('generates correct width and height when both are provided', async () => { - const path = $('.both img').attr('src'); - const { width, height } = await getImageDimensionsFromLocalFile(fixture, path); - assert.equal(width, 300); - assert.equal(height, 400); - }); - - it('generates correct height when only width is provided', async () => { - const path = $('.width-only img').attr('src'); - const { width, height } = await getImageDimensionsFromLocalFile(fixture, path); - assert.equal(width, 300); - assert.equal(height, 200); - }); - - it('generates correct width when only height is provided', async () => { - const path = $('.height-only img').attr('src'); - const { width, height } = await getImageDimensionsFromLocalFile(fixture, path); - assert.equal(width, 600); - assert.equal(height, 400); - }); - - it('preserves aspect ratio when fit=inside', async () => { - const path = $('.fit-inside img').attr('src'); - const { width, height } = await getImageDimensionsFromLocalFile(fixture, path); - assert.equal(width, 300); - assert.equal(height, 200); - }); - - it('preserves aspect ratio when fit=contain', async () => { - const path = $('.fit-contain img').attr('src'); - const { width, height } = await getImageDimensionsFromLocalFile(fixture, path); - assert.equal(width, 300); - assert.equal(height, 200); - }); - - it('preserves aspect ratio when fit=outside', async () => { - const path = $('.fit-outside img').attr('src'); - const { width, height } = await getImageDimensionsFromLocalFile(fixture, path); - assert.equal(width, 600); - assert.equal(height, 400); - }); - const originalWidth = 2316; - const originalHeight = 1544; - it('does not upscale image if requested size is larger than original', async () => { - const path = $('.too-large img').attr('src'); - const { width, height } = await getImageDimensionsFromLocalFile(fixture, path); - assert.equal(width, originalWidth); - assert.equal(height, originalHeight); - }); - }); - }); -}); diff --git a/packages/astro/test/units/assets/600x400.jpg b/packages/astro/test/units/assets/600x400.jpg new file mode 100644 index 000000000000..1388b0116bc5 Binary files /dev/null and b/packages/astro/test/units/assets/600x400.jpg differ diff --git a/packages/astro/test/units/assets/image-service.test.js b/packages/astro/test/units/assets/image-service.test.js new file mode 100644 index 000000000000..61e4702cdf82 --- /dev/null +++ b/packages/astro/test/units/assets/image-service.test.js @@ -0,0 +1,83 @@ +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import { before, describe, it } from 'node:test'; +import { lookup as probe } from '../../../dist/assets/utils/vendor/image-size/lookup.js'; + +// Small local image (600×400) to keep transforms fast without depending on external fixtures +const FIXTURE_IMAGE = new URL('./600x400.jpg', import.meta.url); +const ORIGINAL_WIDTH = 600; +const ORIGINAL_HEIGHT = 400; + +describe('sharp image service', async () => { + const sharpService = (await import('../../../dist/assets/services/sharp.js')).default; + + const config = { service: { entrypoint: '', config: {} } }; + + let inputBuffer; + before(async () => { + inputBuffer = new Uint8Array(await readFile(FIXTURE_IMAGE)); + }); + + async function transform(opts) { + const { data } = await sharpService.transform( + inputBuffer, + { src: 'penguin.jpg', format: 'webp', ...opts }, + config, + ); + return probe(data); + } + + it('generates correct width and height when both are provided', async () => { + const { width, height } = await transform({ width: 300, height: 400, fit: 'cover' }); + assert.equal(width, 300); + assert.equal(height, 400); + }); + + it('generates correct height when only width is provided', async () => { + const { width, height } = await transform({ width: 300 }); + assert.equal(width, 300); + assert.equal(height, 200); + }); + + it('generates correct width when only height is provided', async () => { + const { width, height } = await transform({ height: 400 }); + assert.equal(width, 600); + assert.equal(height, 400); + }); + + it('preserves aspect ratio when fit=inside', async () => { + const { width, height } = await transform({ width: 300, height: 400, fit: 'inside' }); + assert.equal(width, 300); + assert.equal(height, 200); + }); + + it('preserves aspect ratio when fit=contain', async () => { + const { width, height } = await transform({ width: 300, height: 400, fit: 'contain' }); + assert.equal(width, 300); + assert.equal(height, 200); + }); + + it('preserves aspect ratio when fit=outside', async () => { + const { width, height } = await transform({ width: 300, height: 400, fit: 'outside' }); + assert.equal(width, 600); + assert.equal(height, 400); + }); + + it('does not upscale image if requested size is larger than original', async () => { + const { width, height } = await transform({ width: 3000, height: 2000, fit: 'cover' }); + assert.equal(width, ORIGINAL_WIDTH); + assert.equal(height, ORIGINAL_HEIGHT); + }); + + it('does not upscale image if requested size is larger than original and fit is unset', async () => { + const { width, height } = await transform({ width: 3000, height: 2000 }); + assert.equal(width, ORIGINAL_WIDTH); + assert.equal(height, ORIGINAL_HEIGHT); + }); + + it('does not upscale if only one dimension is provided and fit is set', async () => { + const { width, height } = await transform({ width: 3000, fit: 'cover' }); + assert.equal(width, ORIGINAL_WIDTH); + assert.equal(height, ORIGINAL_HEIGHT); + }); +});