Skip to content

Commit

Permalink
Add picture tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ascorbic committed Nov 13, 2024
1 parent 8e2379c commit 7977882
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 5 deletions.
2 changes: 1 addition & 1 deletion packages/astro/components/Picture.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
174 changes: 174 additions & 0 deletions packages/astro/test/core-image-layout.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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())

Check warning on line 328 in packages/astro/test/core-image-layout.test.js

View workflow job for this annotation

GitHub Actions / Lint

lint/suspicious/noConsoleLog

Don't use console.log
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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand All @@ -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;
Expand Down

0 comments on commit 7977882

Please sign in to comment.