mediawiki-extensions-Popups/tests/node-qunit/ui/thumbnail.test.js
Thiemo Kreuz a01c31efd7 Show medium size (<320 wide, but >=250 high) images as well
The requirement for landscape images is to be at least 320px
wide. The requirement for portrait images is more relaxed,
only 250px high. Images that fall between these two
requirements currently don't show a thumbnail, even if they
could.

This change affects a very specific group of images:
* Square images from 250 x 250 to 319 x 319.
* Landscape images from 251 x 250 to 319 x 250.
* Landscape images from 319 x 250 to 319 x 318.

The most extreme ratio is 319 x 250. This will be cut to
203 x 250. I.e. the absolute extreme are 58px missing left
and right, but never more.

Requested at https://www.mediawiki.org/wiki/Topic:Vwl97pm6as9fuf6k

Additional stories for testing more extreme small images:
* Small Tall - 300x1000px
* Small Short - 300x200px

Update tests accordingly.

Bug: T268999
Change-Id: I811f1c0e7e9b0c30280b36a61cc7831a5b9e58c8
2021-08-03 13:17:06 -06:00

348 lines
8.9 KiB
JavaScript

import { createThumbnail, createThumbnailSVG } from '../../../src/ui/thumbnail';
import * as constants from '../../../src/constants';
QUnit.module( 'ext.popups#thumbnail', {
beforeEach() {
this.sandbox.stub( constants.default, 'BRACKETED_DEVICE_PIXEL_RATIO' ).value( 1 );
}
} );
QUnit.test( 'createThumbnail - tall image', ( assert ) => {
const devicePixelRatio = constants.default.BRACKETED_DEVICE_PIXEL_RATIO,
cases = [
{
source: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.jpg/409px-President_Barack_Obama.jpg',
width: 409,
height: 512
},
{
// Per T268999 code review, short/wide images that are less than the portrait width
// requirement are getting the "tall" treatment.
source: 'https://upload.wikimedia.org/wikipedia/en/6/60/Sonic_Adventure.PNG',
width: 300,
height: 297
}
];
cases.forEach( ( case_ ) => {
const thumbnail = createThumbnail( {
source: case_.source,
width: case_.width,
height: case_.height
} );
assert.strictEqual(
thumbnail.isTall,
true,
'Thumbnail is tall.'
);
assert.strictEqual(
thumbnail.width,
thumbnail.width / devicePixelRatio,
'Thumbnail width is correct.'
);
assert.strictEqual(
thumbnail.height,
thumbnail.height / devicePixelRatio,
'Thumbnail height is correct.'
);
} );
} );
QUnit.test( 'createThumbnail - tall image element', ( assert ) => {
const cases = [
{
width: 200,
height: 300,
// If thumbnail is less than SIZES.portraitImage we set x to 0
// per https://phabricator.wikimedia.org/T192928#4312088
expectedIsNarrow: true,
expectedOffset: 3,
expectedX: 0,
expectedY: ( 300 - 250 ) / -2,
// If thumbnail is less than SIZES.portraitImage we shrink thumbnail
// per https://phabricator.wikimedia.org/T192928#4312088
expectedSVGWidth: 200,
expectedSVGHeight: 250,
message: 'Width smaller than the predefined width (203).'
},
{
width: 250,
height: 300,
expectedOffset: 0,
expectedIsNarrow: false,
expectedX: ( 250 - 203 ) / -2,
expectedY: ( 300 - 250 ) / -2,
expectedSVGWidth: 203,
expectedSVGHeight: 250,
message: 'Width bigger than the predefined width (203).'
}
];
cases.forEach( ( case_ ) => {
const thumbnail = createThumbnail( {
source: 'https://image.url',
width: case_.width,
height: case_.height
} );
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'x' ),
case_.expectedX,
`Image element x coordinate is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'y' ),
case_.expectedY,
`Image element y coordinate is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'width' ),
case_.width,
`Image element width is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'height' ),
case_.height,
`Image element height is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.attr( 'width' ),
case_.expectedSVGWidth,
`Image SVG width is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.attr( 'height' ),
case_.expectedSVGHeight,
`Image SVG height is correct. ${case_.message}`
);
assert.strictEqual(
thumbnail.isNarrow,
case_.expectedIsNarrow,
`Image isNarrow is correct. ${case_.message}`
);
assert.strictEqual(
thumbnail.offset,
case_.expectedOffset,
`Image offset is correct. ${case_.message}`
);
} );
} );
QUnit.test( 'createThumbnail - landscape image', ( assert ) => {
const devicePixelRatio = constants.default.BRACKETED_DEVICE_PIXEL_RATIO,
cases = [
{
source: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.jpg/500px-President_Barack_Obama.jpg',
width: 500,
height: 400
},
{
// Per T268999 code review, square images receive horizontal (landscape) treatment
// to be rendered in portrait-mode popups.
source: 'https://image.url',
width: 320,
height: 320
}
];
cases.forEach( ( case_ ) => {
const thumbnail = createThumbnail( {
source: case_.source,
width: case_.width,
height: case_.height
} );
assert.strictEqual(
thumbnail.isTall,
false,
'Thumbnail is not tall.'
);
assert.strictEqual(
thumbnail.width,
thumbnail.width / devicePixelRatio,
'Thumbnail width is correct.'
);
assert.strictEqual(
thumbnail.height,
thumbnail.height / devicePixelRatio,
'Thumbnail height is correct.'
);
} );
} );
QUnit.test( 'createThumbnail - clip-path is supported', ( assert ) => {
const thumbnail = createThumbnail( {
source: 'http://image.url',
width: 320, height: 200
}, true );
assert.strictEqual( thumbnail.el.tagName, 'IMG', 'Using a raw img element' );
} );
QUnit.test( 'createThumbnail - landscape image element', ( assert ) => {
const cases = [
{
width: 400,
height: 150,
expectedX: 0,
expectedY: 0,
expectedSVGWidth: 320,
expectedSVGHeight: 150,
message: 'Height smaller than the predefined height (200).'
},
{
width: 400,
height: 250,
expectedX: 0,
expectedY: ( 250 - 200 ) / -2,
expectedSVGWidth: 320,
expectedSVGHeight: 200,
message: 'Height bigger than the predefined height (200).'
}
];
cases.forEach( ( case_ ) => {
const thumbnail = createThumbnail( {
source: 'https://image.url',
width: case_.width,
height: case_.height
} );
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'x' ),
case_.expectedX,
`Image x coordinate is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'y' ),
case_.expectedY,
`Image y coordinate is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'width' ),
case_.width,
`Image element width is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.find( 'image' ).attr( 'height' ),
case_.height,
`Image element height is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.attr( 'width' ),
case_.expectedSVGWidth,
`Image SVG width is correct. ${case_.message}`
);
assert.strictEqual(
+thumbnail.el.attr( 'height' ),
case_.expectedSVGHeight,
`Image SVG height is correct. ${case_.message}`
);
} );
} );
QUnit.test( 'createThumbnail - no raw thumbnail', ( assert ) => {
const thumbnail = createThumbnail( null );
assert.strictEqual( thumbnail, null, 'No thumbnail.' );
} );
QUnit.test( 'createThumbnail - medium wide image (>=250 high)', ( assert ) => {
const rawThumbnail = {
source: 'https://landscape-image.jpg',
width: 299,
height: 298
},
thumbnail = createThumbnail( rawThumbnail );
assert.notStrictEqual( thumbnail, null, 'There is a thumbnail.' );
} );
QUnit.test( 'createThumbnail - small wide image (<250 high)', ( assert ) => {
const rawThumbnail = {
source: 'https://landscape-image.jpg',
width: 299,
height: 249
},
thumbnail = createThumbnail( rawThumbnail );
assert.strictEqual( thumbnail, null, 'No thumbnail.' );
} );
QUnit.test( 'createThumbnail - small tall image', ( assert ) => {
const rawThumbnail = {
source: 'https://tall-image.jpg',
width: 248,
height: 249
},
thumbnail = createThumbnail( rawThumbnail );
assert.strictEqual( thumbnail, null, 'No thumbnail.' );
} );
QUnit.test( 'createThumbnail - insecure URL', ( assert ) => {
const cases = [
'https://tall-ima\\ge.jpg',
'https://tall-ima\'ge.jpg',
'https://tall-ima"ge.jpg'
];
cases.forEach( ( case_ ) => {
const thumbnail = createThumbnail( {
source: case_,
width: 500,
height: 400
} );
assert.strictEqual( thumbnail, null, 'No thumbnail.' );
} );
} );
QUnit.test( 'createThumbnailSVG', ( assert ) => {
const
url = 'https://thumbnail.url',
x = 25,
y = 50,
thumbnailWidth = 200,
thumbnailHeight = 250,
width = 500,
height = 300;
[
{
className: 'mwe-popups-is-not-tall',
expectedPoints: '0 299 500 299',
expectedHTML: '<image href="https://thumbnail.url" class="mwe-popups-is-not-tall" x="25" y="50" width="200" height="250"></image>'
},
{
className: 'mwe-popups-is-tall',
expectedPoints: '0 0 0 300'
}
].forEach( ( { className, expectedPoints, expectedHTML }, i ) => {
const $thumbnail = createThumbnailSVG(
className, url, x, y, thumbnailWidth, thumbnailHeight,
width, height );
// Simplify HTML image test
const points = $thumbnail.find( 'polyline' ).attr( 'points' );
$thumbnail.find( 'polyline' ).remove();
assert.strictEqual( points, expectedPoints, 'Points are correct.' );
if ( expectedHTML ) {
assert.strictEqual(
$thumbnail.html(),
expectedHTML,
'Thumbnail HTML is correct.'
);
}
if ( i === 0 ) {
assert.strictEqual(
$thumbnail.attr( 'xmlns' ),
'http://www.w3.org/2000/svg',
'SVG namespace is correct.'
);
assert.equal( $thumbnail.attr( 'height' ), height, 'SVG height is correct.' );
assert.equal( $thumbnail.attr( 'width' ), width, 'SVG width is correct.' );
}
} );
} );