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:
parent
8e2379c0c1
commit
7977882481
3 changed files with 196 additions and 5 deletions
|
@ -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 (
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue