0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

Add picture tests

This commit is contained in:
Matt Kane 2024-11-13 14:02:56 +00:00
parent 8e2379c0c1
commit 7977882481
3 changed files with 196 additions and 5 deletions

View file

@ -125,7 +125,7 @@ if (import.meta.env.DEV) {
{
Object.entries(optimizedImages).map(([_, image]) => {
const srcsetAttribute =
props.densities || (!props.densities && !props.widths)
props.densities || (!props.densities && !props.widths && !useResponsive)
? `${image.src}${image.srcSet.values.length > 0 ? ', ' + image.srcSet.attribute : ''}`
: image.srcSet.attribute;
return (

View file

@ -273,6 +273,180 @@ describe('astro:image:layout', () => {
});
});
});
describe('picture component', () => {
/** Original image dimensions */
const originalWidth = 2316;
const originalHeight = 1544;
/** @type {import("cheerio").CheerioAPI} */
let $;
before(async () => {
let res = await fixture.fetch('/picture');
let html = await res.text();
$ = cheerio.load(html);
});
describe('basics', () => {
it('creates picture and img elements', () => {
let $picture = $('#picture-density-2-format picture');
let $img = $('#picture-density-2-format img');
assert.equal($picture.length, 1);
assert.equal($img.length, 1);
});
it('includes source elements for each format', () => {
let $sources = $('#picture-density-2-format source');
assert.equal($sources.length, 2); // avif and webp formats
const types = $sources.map((_, el) => $(el).attr('type')).get();
assert.deepEqual(types.sort(), ['image/avif', 'image/webp']);
});
it('generates responsive srcset matching layout breakpoints', () => {
let $source = $('#picture-density-2-format source').first();
const srcset = parseSrcset($source.attr('srcset'));
const widths = srcset.map(s => s.w);
assert.deepEqual(widths, [640, 750, 828, 1080, 1158, 1280, 1668, 2048, 2316]);
});
it('has proper width and height attributes', () => {
let $img = $('#picture-density-2-format img');
// Width is set to half of original in the component
const expectedWidth = Math.round(originalWidth / 2);
const expectedHeight = Math.round((originalHeight / 2));
assert.equal($img.attr('width'), expectedWidth.toString());
assert.equal($img.attr('height'), expectedHeight.toString());
});
});
describe('responsive variants', () => {
it('constrained - has max of 2x requested size', () => {
let $source = $('#picture-constrained source').first();
console.log($source.html())
const widths = parseSrcset($source.attr('srcset')).map(s => s.w);
assert.equal(widths.at(-1), 1600); // Max should be 2x the 800px width
let $img = $('#picture-constrained img');
const aspectRatio = originalWidth / originalHeight;
assert.equal($img.attr('width'), '800');
assert.equal($img.attr('height'), Math.round(800 / aspectRatio).toString());
});
it('constrained - just has 1x and 2x when smaller than min breakpoint', () => {
let $source = $('#picture-both source').first();
const widths = parseSrcset($source.attr('srcset')).map(s => s.w);
assert.deepEqual(widths, [300, 600]); // Just 1x and 2x for small images
let $img = $('#picture-both img');
assert.equal($img.attr('width'), '300');
assert.equal($img.attr('height'), '400');
});
it('fixed - has just 1x and 2x', () => {
let $source = $('#picture-fixed source').first();
const widths = parseSrcset($source.attr('srcset')).map(s => s.w);
assert.deepEqual(widths, [400, 800]); // Fixed layout only needs 1x and 2x
let $img = $('#picture-fixed img');
assert.equal($img.attr('width'), '400');
assert.equal($img.attr('height'), '300');
});
it('full-width: has all breakpoints below image size', () => {
let $source = $('#picture-full-width source').first();
const widths = parseSrcset($source.attr('srcset')).map(s => s.w);
assert.deepEqual(widths, [640, 750, 828, 1080, 1280, 1668, 2048]);
});
});
describe('fallback format', () => {
it('uses specified fallback format', () => {
let $img = $('#picture-fallback img');
const imageURL = new URL($img.attr('src'), 'http://localhost');
assert.equal(imageURL.searchParams.get('f'), 'jpeg');
});
it('does not add fallbackFormat as an attribute', () => {
let $img = $('#picture-fallback img');
assert.equal($img.attr('fallbackformat'), undefined);
});
it('maintains original aspect ratio', () => {
let $img = $('#picture-fallback img');
const width = parseInt($img.attr('width'));
const height = parseInt($img.attr('height'));
const imageAspectRatio = width / height;
const originalAspectRatio = originalWidth / originalHeight;
// Allow for small rounding differences
assert.ok(Math.abs(imageAspectRatio - originalAspectRatio) < 0.01);
});
});
describe('attributes', () => {
it('applies class to img element', () => {
let $img = $('#picture-attributes img');
assert.ok($img.attr('class').includes('img-comp'));
});
it('applies pictureAttributes to picture element', () => {
let $picture = $('#picture-attributes picture');
assert.ok($picture.attr('class').includes('picture-comp'));
});
it('maintains inline style attributes', () => {
let $img = $('#picture-attributes img');
const style = $img.attr('style');
assert.match(style, /--w:/);
assert.match(style, /--h:/);
});
});
describe('MIME types', () => {
it('creates source elements with correct MIME types', () => {
const $sources = $('#picture-mime-types source');
const types = $sources.map((_, el) => $(el).attr('type')).get();
// Should have all specified formats in correct MIME type format
const expectedTypes = [
// Included twice because we pass jpg and jpeg
'image/jpeg',
'image/jpeg',
'image/png',
'image/avif',
'image/webp'
];
assert.deepEqual(
types.sort(),
expectedTypes.sort()
);
});
it('uses valid MIME type format', () => {
const $sources = $('#picture-mime-types source');
const validMimeTypes = [
'image/webp',
'image/jpeg',
'image/avif',
'image/png',
'image/gif',
'image/svg+xml'
];
$sources.each((_, source) => {
const type = $(source).attr('type');
assert.ok(
validMimeTypes.includes(type),
`Expected type attribute value to be a valid MIME type: ${type}`
);
});
});
});
});
});
describe('remote image service', () => {

View file

@ -4,12 +4,9 @@ import myImage from "../assets/penguin.jpg";
---
<div id="picture-density-2-format">
<Picture src={myImage} width={Math.round(myImage.width / 2)} alt="A penguin" formats={['avif', 'webp']} />
<Picture src={myImage} width={Math.floor(myImage.width / 2)} alt="A penguin" formats={['avif', 'webp']} />
</div>
<div id="picture-widths">
<Picture src={myImage} width={Math.round(myImage.width / 2)} alt="A penguin" widths={[myImage.width]} sizes="(max-width: 448px) 400px, (max-width: 810px) 750px, 1050px" />
</div>
<div id="picture-fallback">
<Picture src={myImage} fallbackFormat="jpeg" alt="A penguin" />
@ -23,6 +20,26 @@ import myImage from "../assets/penguin.jpg";
<Picture alt="A penguin" src={myImage} formats={['jpg', 'jpeg', 'png', 'avif', 'webp']} />
</div>
<div id="picture-constrained">
<Picture src={myImage} width={800} alt="A penguin" />
</div>
<div id="picture-small">
<Picture src={myImage} width={300} alt="A penguin" />
</div>
<div id="picture-both">
<Picture src={myImage} width={300} height={400} alt="A penguin" />
</div>
<div id="picture-fixed">
<Picture src={myImage} width={400} height={300} layout="fixed" alt="A penguin" />
</div>
<div id="picture-full-width">
<Picture src={myImage} layout="full-width" alt="A penguin" />
</div>
<style>
.img-comp {
border: 5px solid blue;