mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/MultimediaViewer
synced 2024-12-25 06:04:13 +00:00
44b15a2c2c
This test was leaving behind the black overlay, obscuring the test results interface. Upon closer inspection, it appears not one part of this test is doing anything that actually works. * Assertion 1: `viewer.isOpen === false` This is the initial and default state. Not essential for the test, but so far so good. * Prep for Assertion 2 & 3: We call loadImageByTitle. One might think that this will set isOpen to true, but, this isn't asserted. If one did, the test would fail, because loadImageByTitle returns early from its first branch in `!this.thumbs.length` and is thus a no-op, and adds nothing to the test. We set `location.hash = 'Foo'`. * Assertion 2: `location.hash === '#Foo'` Assertion 3: `viewer.isOpen === false` Assertion 2 is meaningless because handling of hash changes appears to be asynchronous. Perhaps not always, but at least the indirection used in this codebase makes it async. Hence, the fact that it is correctly normalised to '#Foo' is meaningless as the popstate and hashchange event handler haven't been processed yet. If they were, it would still be meaningless since viewer.isOpen was never true in the first place and MMV's event handlers for popstate/hashchange are guarded by `isOpen`. * Prep for Assertion 4: location.hash = Bar * Assertion 4: `location.hash === '#Bar'` Idem. * Prep for Assertion 5: Replace the viewer.loadImageByTitle function with a function that performs a nested assertion. This function is never called and its assertion never reached. This is a textbook example why assertions should never be nested. If you rewrite this in following with the advice from <https://api.qunitjs.com/assert/verifySteps/>, we find that the array is in fact empty by the end of the test() function. ```js var seen = []; viewer.loadImageByTitle = function ( title ) { // assert.strictEqual( … ); seen.push( title.getPrefixedText() ); }; location.hash = …; location.hash = …; location.hash = …; location.hash = …; assert.deepEqual( seen, [ 'File:' + imageSrc' } ); // Actual: [] ``` == Misc notes == The test ends with "#/media/File:Foo bar.jpg" set on location.hash, and the black overlay covering the page. It seems that the hashchange are async, and that the first in a given event loop tick "wins", with later ones being ignored. Observing this with debugger/breakpoints is hard given that each pause results in the effect not being observable. console.log(location.href) at the end of the test shows hash='#', but placing a debugger at the end of the test results in the hash being "#/media/…" instead. Exprienced in Firefox 117 on macOS. Change-Id: Ib37bec1b3e67fcab1da89d23381a3adbb6312827
648 lines
27 KiB
JavaScript
648 lines
27 KiB
JavaScript
const { MultimediaViewer, Thumbnail } = require( 'mmv' );
|
|
const { getMultimediaViewer } = require( './mmv.testhelpers.js' );
|
|
const { MultimediaViewerBootstrap } = require( 'mmv.bootstrap' );
|
|
|
|
( function () {
|
|
QUnit.module( 'mmv', QUnit.newMwEnvironment( {
|
|
beforeEach: function () {
|
|
// prevent a real "back" navigation from taking place
|
|
this.sandbox.stub( require( 'mediawiki.router' ), 'back' );
|
|
}
|
|
} ) );
|
|
|
|
QUnit.test( 'eachPreloadableLightboxIndex()', function ( assert ) {
|
|
var viewer = getMultimediaViewer(),
|
|
expectedIndices,
|
|
i;
|
|
|
|
viewer.preloadDistance = 3;
|
|
viewer.thumbs = [];
|
|
|
|
// 0..10
|
|
for ( i = 0; i < 11; i++ ) {
|
|
viewer.thumbs.push( { image: false } );
|
|
}
|
|
|
|
viewer.currentIndex = 2;
|
|
i = 0;
|
|
expectedIndices = [ 2, 3, 1, 4, 0, 5 ];
|
|
viewer.eachPreloadableLightboxIndex( function ( index ) {
|
|
assert.strictEqual( index, expectedIndices[ i++ ], 'preload on left edge' );
|
|
} );
|
|
|
|
viewer.currentIndex = 9;
|
|
i = 0;
|
|
expectedIndices = [ 9, 10, 8, 7, 6 ];
|
|
viewer.eachPreloadableLightboxIndex( function ( index ) {
|
|
assert.strictEqual( index, expectedIndices[ i++ ], 'preload on right edge' );
|
|
} );
|
|
} );
|
|
|
|
QUnit.test( 'Progress', function ( assert ) {
|
|
var imageDeferred = $.Deferred(),
|
|
viewer = getMultimediaViewer(),
|
|
fakeImage = {
|
|
filePageTitle: new mw.Title( 'File:Stuff.jpg' ),
|
|
extraStatsDeferred: $.Deferred().reject()
|
|
},
|
|
// custom clock ensures progress handlers execute in correct sequence
|
|
clock = this.sandbox.useFakeTimers();
|
|
|
|
viewer.thumbs = [];
|
|
viewer.displayPlaceholderThumbnail = function () {};
|
|
viewer.setImage = function () {};
|
|
viewer.scroll = function () {};
|
|
viewer.preloadFullscreenThumbnail = function () {};
|
|
viewer.fetchSizeIndependentLightboxInfo = () => $.Deferred().resolve( {} );
|
|
viewer.ui = {
|
|
setFileReuseData: function () {},
|
|
setupForLoad: function () {},
|
|
canvas: { set: function () {},
|
|
unblurWithAnimation: function () {},
|
|
unblur: function () {},
|
|
getCurrentImageWidths: function () { return { real: 0 }; },
|
|
getDimensions: function () { return {}; }
|
|
},
|
|
panel: {
|
|
setImageInfo: function () {},
|
|
scroller: {
|
|
animateMetadataOnce: function () {}
|
|
},
|
|
progressBar: {
|
|
hide: this.sandbox.stub(),
|
|
animateTo: this.sandbox.stub(),
|
|
jumpTo: this.sandbox.stub()
|
|
}
|
|
},
|
|
open: function () {} };
|
|
|
|
viewer.imageProvider.get = () => imageDeferred.promise();
|
|
viewer.imageInfoProvider.get = () => $.Deferred().resolve( {} );
|
|
viewer.thumbnailInfoProvider.get = () => $.Deferred().resolve( {} );
|
|
|
|
// loadImage will call setupProgressBar, which will attach done, fail &
|
|
// progress handlers
|
|
viewer.loadImage( fakeImage, new Image() );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.jumpTo.lastCall.calledWith( 0 ), true,
|
|
'Percentage correctly reset by loadImage' );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.firstCall.calledWith( 5 ), true,
|
|
'Percentage correctly animated to 5 by loadImage' );
|
|
|
|
imageDeferred.notify( 'response', 45 );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.secondCall.calledWith( 45 ), true,
|
|
'Percentage correctly funneled to panel UI' );
|
|
|
|
imageDeferred.resolve( {}, {} );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.thirdCall.calledWith( 100 ), true,
|
|
'Percentage correctly funneled to panel UI' );
|
|
|
|
clock.restore();
|
|
|
|
viewer.close();
|
|
} );
|
|
|
|
QUnit.test( 'Progress when switching images', function ( assert ) {
|
|
var firstImageDeferred = $.Deferred(),
|
|
secondImageDeferred = $.Deferred(),
|
|
firstImage = {
|
|
index: 1,
|
|
filePageTitle: new mw.Title( 'File:First.jpg' ),
|
|
extraStatsDeferred: $.Deferred().reject()
|
|
},
|
|
secondImage = {
|
|
index: 2,
|
|
filePageTitle: new mw.Title( 'File:Second.jpg' ),
|
|
extraStatsDeferred: $.Deferred().reject()
|
|
},
|
|
viewer = getMultimediaViewer(),
|
|
// custom clock ensures progress handlers execute in correct sequence
|
|
clock = this.sandbox.useFakeTimers();
|
|
|
|
// animation would keep running, conflict with other tests
|
|
this.sandbox.stub( $.fn, 'animate' ).returnsThis();
|
|
|
|
viewer.thumbs = [];
|
|
viewer.displayPlaceholderThumbnail = function () {};
|
|
viewer.setImage = function () {};
|
|
viewer.scroll = function () {};
|
|
viewer.preloadFullscreenThumbnail = function () {};
|
|
viewer.preloadImagesMetadata = function () {};
|
|
viewer.preloadThumbnails = function () {};
|
|
viewer.fetchSizeIndependentLightboxInfo = () => $.Deferred().resolve( {} );
|
|
viewer.ui = {
|
|
setFileReuseData: function () {},
|
|
setupForLoad: function () {},
|
|
canvas: { set: function () {},
|
|
unblurWithAnimation: function () {},
|
|
unblur: function () {},
|
|
getCurrentImageWidths: function () { return { real: 0 }; },
|
|
getDimensions: function () { return {}; }
|
|
},
|
|
panel: {
|
|
setImageInfo: function () {},
|
|
scroller: {
|
|
animateMetadataOnce: function () {}
|
|
},
|
|
progressBar: {
|
|
hide: this.sandbox.stub(),
|
|
animateTo: this.sandbox.stub(),
|
|
jumpTo: this.sandbox.stub()
|
|
}
|
|
},
|
|
open: function () {},
|
|
empty: function () {} };
|
|
|
|
viewer.imageInfoProvider.get = () => $.Deferred().resolve( {} );
|
|
viewer.thumbnailInfoProvider.get = () => $.Deferred().resolve( {} );
|
|
|
|
// load some image
|
|
viewer.imageProvider.get = this.sandbox.stub().returns( firstImageDeferred );
|
|
viewer.loadImage( firstImage, new Image() );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.jumpTo.getCall( 0 ).calledWith( 0 ), true,
|
|
'Percentage correctly reset for new first image' );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.getCall( 0 ).calledWith( 5 ), true,
|
|
'Percentage correctly animated to 5 for first new image' );
|
|
|
|
// progress on active image
|
|
firstImageDeferred.notify( 'response', 20 );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.getCall( 1 ).calledWith( 20 ), true,
|
|
'Percentage correctly animated when active image is loading' );
|
|
|
|
// change to another image
|
|
viewer.imageProvider.get = this.sandbox.stub().returns( secondImageDeferred );
|
|
viewer.loadImage( secondImage, new Image() );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.jumpTo.getCall( 1 ).calledWith( 0 ), true,
|
|
'Percentage correctly reset for second new image' );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.getCall( 2 ).calledWith( 5 ), true,
|
|
'Percentage correctly animated to 5 for second new image' );
|
|
|
|
// progress on active image
|
|
secondImageDeferred.notify( 'response', 30 );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.getCall( 3 ).calledWith( 30 ), true,
|
|
'Percentage correctly animated when active image is loading' );
|
|
|
|
// progress on inactive image
|
|
firstImageDeferred.notify( 'response', 40 );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.callCount, 4,
|
|
'Percentage not animated when inactive image is loading' );
|
|
|
|
// progress on active image
|
|
secondImageDeferred.notify( 'response', 50 );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.getCall( 4 ).calledWith( 50 ), true,
|
|
'Percentage correctly ignored inactive image & only animated when active image is loading' );
|
|
|
|
// change back to first image
|
|
viewer.imageProvider.get = this.sandbox.stub().returns( firstImageDeferred );
|
|
viewer.loadImage( firstImage, new Image() );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.jumpTo.getCall( 2 ).calledWith( 40 ), true,
|
|
'Percentage jumps to right value when changing images' );
|
|
|
|
secondImageDeferred.resolve( {}, {} );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.hide.called, false,
|
|
'Progress bar not hidden when something finishes in the background' );
|
|
|
|
// change back to second image, which has finished loading
|
|
viewer.imageProvider.get = this.sandbox.stub().returns( secondImageDeferred );
|
|
viewer.loadImage( secondImage, new Image() );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.hide.called, true,
|
|
'Progress bar hidden when switching to finished image' );
|
|
|
|
clock.restore();
|
|
|
|
viewer.close();
|
|
} );
|
|
|
|
QUnit.test( 'resetBlurredThumbnailStates', function ( assert ) {
|
|
var viewer = getMultimediaViewer();
|
|
|
|
// animation would keep running, conflict with other tests
|
|
this.sandbox.stub( $.fn, 'animate' ).returnsThis();
|
|
|
|
assert.strictEqual( viewer.realThumbnailShown, false, 'Real thumbnail state is correct' );
|
|
assert.strictEqual( viewer.blurredThumbnailShown, false, 'Placeholder state is correct' );
|
|
|
|
viewer.realThumbnailShown = true;
|
|
viewer.blurredThumbnailShown = true;
|
|
|
|
viewer.resetBlurredThumbnailStates();
|
|
|
|
assert.strictEqual( viewer.realThumbnailShown, false, 'Real thumbnail state is correct' );
|
|
assert.strictEqual( viewer.blurredThumbnailShown, false, 'Placeholder state is correct' );
|
|
} );
|
|
|
|
QUnit.test( 'Placeholder first, then real thumbnail', function ( assert ) {
|
|
var viewer = getMultimediaViewer();
|
|
|
|
viewer.setImage = function () {};
|
|
viewer.ui = { canvas: {
|
|
unblurWithAnimation: function () {},
|
|
unblur: function () {},
|
|
maybeDisplayPlaceholder: function () { return true; }
|
|
} };
|
|
viewer.imageInfoProvider.get = this.sandbox.stub();
|
|
|
|
viewer.displayPlaceholderThumbnail( { originalWidth: 100, originalHeight: 100 }, undefined, undefined );
|
|
|
|
assert.strictEqual( viewer.blurredThumbnailShown, true, 'Placeholder state is correct' );
|
|
assert.strictEqual( viewer.realThumbnailShown, false, 'Real thumbnail state is correct' );
|
|
|
|
viewer.displayRealThumbnail( { url: undefined } );
|
|
|
|
assert.strictEqual( viewer.realThumbnailShown, true, 'Real thumbnail state is correct' );
|
|
assert.strictEqual( viewer.blurredThumbnailShown, true, 'Placeholder state is correct' );
|
|
} );
|
|
|
|
QUnit.test( 'Placeholder first, then real thumbnail - missing size', function ( assert ) {
|
|
var viewer = getMultimediaViewer();
|
|
|
|
viewer.currentIndex = 1;
|
|
viewer.setImage = function () {};
|
|
viewer.ui = { canvas: {
|
|
unblurWithAnimation: function () {},
|
|
unblur: function () {},
|
|
maybeDisplayPlaceholder: function () { return true; }
|
|
} };
|
|
viewer.imageInfoProvider.get = this.sandbox.stub().returns( $.Deferred().resolve( { width: 100, height: 100 } ) );
|
|
|
|
viewer.displayPlaceholderThumbnail( { index: 1 }, undefined, undefined );
|
|
|
|
assert.strictEqual( viewer.blurredThumbnailShown, true, 'Placeholder state is correct' );
|
|
assert.strictEqual( viewer.realThumbnailShown, false, 'Real thumbnail state is correct' );
|
|
|
|
viewer.displayRealThumbnail( { url: undefined } );
|
|
|
|
assert.strictEqual( viewer.realThumbnailShown, true, 'Real thumbnail state is correct' );
|
|
assert.strictEqual( viewer.blurredThumbnailShown, true, 'Placeholder state is correct' );
|
|
} );
|
|
|
|
QUnit.test( 'Real thumbnail first, then placeholder', function ( assert ) {
|
|
var viewer = getMultimediaViewer();
|
|
|
|
viewer.setImage = function () {};
|
|
viewer.ui = {
|
|
showImage: function () {},
|
|
canvas: {
|
|
unblurWithAnimation: function () {},
|
|
unblur: function () {}
|
|
} };
|
|
|
|
viewer.displayRealThumbnail( { url: undefined } );
|
|
|
|
assert.strictEqual( viewer.realThumbnailShown, true, 'Real thumbnail state is correct' );
|
|
assert.strictEqual( viewer.blurredThumbnailShown, false, 'Placeholder state is correct' );
|
|
|
|
viewer.displayPlaceholderThumbnail( {}, undefined, undefined );
|
|
|
|
assert.strictEqual( viewer.realThumbnailShown, true, 'Real thumbnail state is correct' );
|
|
assert.strictEqual( viewer.blurredThumbnailShown, false, 'Placeholder state is correct' );
|
|
} );
|
|
|
|
QUnit.test( 'displayRealThumbnail', function ( assert ) {
|
|
var viewer = getMultimediaViewer();
|
|
|
|
viewer.setImage = function () {};
|
|
viewer.ui = { canvas: {
|
|
unblurWithAnimation: this.sandbox.stub(),
|
|
unblur: function () {}
|
|
} };
|
|
viewer.blurredThumbnailShown = true;
|
|
|
|
// Should not result in an unblurWithAnimation animation (image cache from cache)
|
|
viewer.displayRealThumbnail( { url: undefined }, undefined, undefined, 5 );
|
|
assert.strictEqual( viewer.ui.canvas.unblurWithAnimation.called, false, 'There should not be an unblurWithAnimation animation' );
|
|
|
|
// Should result in an unblurWithAnimation (image didn't come from cache)
|
|
viewer.displayRealThumbnail( { url: undefined }, undefined, undefined, 1000 );
|
|
assert.strictEqual( viewer.ui.canvas.unblurWithAnimation.called, true, 'There should be an unblurWithAnimation animation' );
|
|
} );
|
|
|
|
QUnit.test( 'New image loaded while another one is loading', function ( assert ) {
|
|
var viewer = getMultimediaViewer(),
|
|
firstImageDeferred = $.Deferred(),
|
|
secondImageDeferred = $.Deferred(),
|
|
firstLigthboxInfoDeferred = $.Deferred(),
|
|
secondLigthboxInfoDeferred = $.Deferred(),
|
|
firstImage = {
|
|
filePageTitle: new mw.Title( 'File:Foo.jpg' ),
|
|
index: 0,
|
|
extraStatsDeferred: $.Deferred().reject()
|
|
},
|
|
secondImage = {
|
|
filePageTitle: new mw.Title( 'File:Bar.jpg' ),
|
|
index: 1,
|
|
extraStatsDeferred: $.Deferred().reject()
|
|
},
|
|
// custom clock ensures progress handlers execute in correct sequence
|
|
clock = this.sandbox.useFakeTimers();
|
|
|
|
viewer.preloadFullscreenThumbnail = function () {};
|
|
viewer.fetchSizeIndependentLightboxInfo = this.sandbox.stub();
|
|
viewer.ui = {
|
|
setFileReuseData: function () {},
|
|
setupForLoad: function () {},
|
|
canvas: {
|
|
set: function () {},
|
|
getCurrentImageWidths: function () { return { real: 0 }; },
|
|
getDimensions: function () { return {}; }
|
|
},
|
|
panel: {
|
|
setImageInfo: this.sandbox.stub(),
|
|
scroller: {
|
|
animateMetadataOnce: function () {}
|
|
},
|
|
progressBar: {
|
|
hide: this.sandbox.stub(),
|
|
animateTo: this.sandbox.stub(),
|
|
jumpTo: this.sandbox.stub()
|
|
},
|
|
empty: function () {}
|
|
},
|
|
open: function () {},
|
|
empty: function () {} };
|
|
viewer.displayRealThumbnail = this.sandbox.stub();
|
|
viewer.eachPreloadableLightboxIndex = function () {};
|
|
viewer.animateMetadataDivOnce = this.sandbox.stub().returns( $.Deferred().reject() );
|
|
viewer.imageProvider.get = this.sandbox.stub();
|
|
viewer.imageInfoProvider.get = () => $.Deferred().reject( {} );
|
|
viewer.thumbnailInfoProvider.get = () => $.Deferred().resolve( {} );
|
|
|
|
viewer.imageProvider.get.returns( firstImageDeferred.promise() );
|
|
viewer.fetchSizeIndependentLightboxInfo.returns( firstLigthboxInfoDeferred.promise() );
|
|
viewer.loadImage( firstImage, new Image() );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.animateMetadataDivOnce.called, false, 'Metadata of the first image should not be animated' );
|
|
assert.strictEqual( viewer.ui.panel.setImageInfo.called, false, 'Metadata of the first image should not be shown' );
|
|
|
|
viewer.imageProvider.get.returns( secondImageDeferred.promise() );
|
|
viewer.fetchSizeIndependentLightboxInfo.returns( secondLigthboxInfoDeferred.promise() );
|
|
viewer.loadImage( secondImage, new Image() );
|
|
clock.tick( 10 );
|
|
|
|
viewer.ui.panel.progressBar.animateTo.reset();
|
|
firstImageDeferred.notify( undefined, 45 );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.ui.panel.progressBar.animateTo.reset.called, undefined, 'Progress of the first image should not be shown' );
|
|
|
|
firstImageDeferred.resolve( {}, {} );
|
|
firstLigthboxInfoDeferred.resolve( {} );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.displayRealThumbnail.called, false, 'The first image being done loading should have no effect' );
|
|
|
|
viewer.displayRealThumbnail = this.sandbox.spy( () => viewer.close() );
|
|
secondImageDeferred.resolve( {}, {} );
|
|
secondLigthboxInfoDeferred.resolve( {} );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( viewer.displayRealThumbnail.called, true, 'The second image being done loading should result in the image being shown' );
|
|
|
|
clock.restore();
|
|
} );
|
|
|
|
QUnit.test( 'Events are not trapped after the viewer is closed', function ( assert ) {
|
|
var i, j, k, eventParameters,
|
|
viewer = getMultimediaViewer(),
|
|
$document = $( document ),
|
|
$qf = $( '#qunit-fixture' ),
|
|
eventTypes = [ 'keydown', 'keyup', 'keypress', 'click', 'mousedown', 'mouseup' ],
|
|
modifiers = [ undefined, 'altKey', 'ctrlKey', 'shiftKey', 'metaKey' ],
|
|
// Events are async, we need to wait for the last event to be caught before ending the test
|
|
done = assert.async(),
|
|
oldScrollTo = $.scrollTo;
|
|
|
|
assert.expect( 0 );
|
|
|
|
// animation would keep running, conflict with other tests
|
|
this.sandbox.stub( $.fn, 'animate' ).returnsThis();
|
|
|
|
$.scrollTo = function () {
|
|
return { scrollTop: () => {}, on: () => {}, off: () => {} };
|
|
};
|
|
|
|
viewer.setupEventHandlers();
|
|
|
|
viewer.imageProvider.get = () => $.Deferred().reject();
|
|
viewer.imageInfoProvider.get = () => $.Deferred().reject();
|
|
viewer.thumbnailInfoProvider.get = () => $.Deferred().reject();
|
|
viewer.fileRepoInfoProvider.get = () => $.Deferred().reject();
|
|
|
|
viewer.preloadFullscreenThumbnail = function () {};
|
|
viewer.initWithThumbs( [] );
|
|
|
|
viewer.loadImage(
|
|
{
|
|
filePageTitle: new mw.Title( 'File:Stuff.jpg' ),
|
|
thumbnail: new Thumbnail( 'foo', 10, 10 ),
|
|
extraStatsDeferred: $.Deferred().reject()
|
|
},
|
|
new Image()
|
|
);
|
|
|
|
viewer.ui.$closeButton.trigger( 'click' );
|
|
|
|
function eventHandler( e ) {
|
|
if ( e.isDefaultPrevented() ) {
|
|
assert.true( false, 'Event was incorrectly trapped: ' + e.which );
|
|
}
|
|
|
|
e.preventDefault();
|
|
|
|
// Wait for the last event
|
|
if ( e.which === 32 && e.type === 'mouseup' ) {
|
|
$document.off( '.mmvtest' );
|
|
viewer.cleanupEventHandlers();
|
|
$.scrollTo = oldScrollTo;
|
|
done();
|
|
}
|
|
}
|
|
|
|
for ( j = 0; j < eventTypes.length; j++ ) {
|
|
$document.on( eventTypes[ j ] + '.mmvtest', eventHandler );
|
|
|
|
eventloop:
|
|
for ( i = 0; i < 256; i++ ) {
|
|
// Save some time by not testing unlikely values for mouse events
|
|
if ( i > 32 ) {
|
|
switch ( eventTypes[ j ] ) {
|
|
case 'click':
|
|
case 'mousedown':
|
|
case 'mouseup':
|
|
break eventloop;
|
|
}
|
|
}
|
|
|
|
for ( k = 0; k < modifiers.length; k++ ) {
|
|
eventParameters = { which: i };
|
|
if ( modifiers[ k ] !== undefined ) {
|
|
eventParameters[ modifiers[ k ] ] = true;
|
|
}
|
|
$qf.trigger( $.Event( eventTypes[ j ], eventParameters ) );
|
|
}
|
|
}
|
|
}
|
|
} );
|
|
|
|
QUnit.test( 'Refuse to load too-big thumbnails', function ( assert ) {
|
|
var title, expectedWidth,
|
|
reuestedWidth = 1000,
|
|
originalWidth = 50,
|
|
viewer = getMultimediaViewer();
|
|
|
|
viewer.thumbnailInfoProvider.get = function ( fileTitle, width ) {
|
|
assert.strictEqual( width, expectedWidth );
|
|
return $.Deferred().reject();
|
|
};
|
|
|
|
// non-vector should be capped to original size
|
|
title = mw.Title.newFromText( 'File:Foobar.png' );
|
|
expectedWidth = originalWidth;
|
|
viewer.fetchThumbnail( title, reuestedWidth, null, originalWidth, 60 );
|
|
|
|
// vector images can be aritrarily large
|
|
title = mw.Title.newFromText( 'File:Foobar.svg' );
|
|
expectedWidth = reuestedWidth;
|
|
viewer.fetchThumbnail( title, reuestedWidth, null, originalWidth, 60 );
|
|
} );
|
|
|
|
QUnit.test( 'fetchThumbnail()', function ( assert ) {
|
|
var guessedThumbnailInfoStub,
|
|
thumbnailInfoStub,
|
|
imageStub,
|
|
promise,
|
|
useThumbnailGuessing,
|
|
viewer = new MultimediaViewer( { imageQueryParameter: function () {}, language: function () {}, recordVirtualViewBeaconURI: function () {}, extensions: function () { return { jpg: 'default' }; }, useThumbnailGuessing: () => useThumbnailGuessing } ),
|
|
sandbox = this.sandbox,
|
|
file = new mw.Title( 'File:Copyleft.svg' ),
|
|
sampleURL = 'http://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Copyleft.svg/300px-Copyleft.svg.png',
|
|
width = 100,
|
|
originalWidth = 1000,
|
|
originalHeight = 1000,
|
|
image = {},
|
|
// custom clock ensures progress handlers execute in correct sequence
|
|
clock = this.sandbox.useFakeTimers();
|
|
|
|
function setupStubs() {
|
|
guessedThumbnailInfoStub = viewer.guessedThumbnailInfoProvider.get = sandbox.stub();
|
|
thumbnailInfoStub = viewer.thumbnailInfoProvider.get = sandbox.stub();
|
|
imageStub = viewer.imageProvider.get = sandbox.stub();
|
|
}
|
|
|
|
useThumbnailGuessing = true;
|
|
|
|
// When we lack sample URL and original dimensions, the classic provider should be used
|
|
setupStubs();
|
|
guessedThumbnailInfoStub.returns( $.Deferred().resolve( { url: 'guessedURL' } ) );
|
|
thumbnailInfoStub.returns( $.Deferred().resolve( { url: 'apiURL' } ) );
|
|
imageStub.returns( $.Deferred().resolve( image ) );
|
|
promise = viewer.fetchThumbnail( file, width );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( guessedThumbnailInfoStub.called, false, 'When we lack sample URL and original dimensions, GuessedThumbnailInfoProvider is not called' );
|
|
assert.strictEqual( thumbnailInfoStub.calledOnce, true, 'When we lack sample URL and original dimensions, ThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( imageStub.calledOnce, true, 'When we lack sample URL and original dimensions, ImageProvider is called once' );
|
|
assert.strictEqual( imageStub.calledWith( 'apiURL' ), true, 'When we lack sample URL and original dimensions, ImageProvider is called with the API url' );
|
|
assert.strictEqual( promise.state(), 'resolved', 'When we lack sample URL and original dimensions, fetchThumbnail resolves' );
|
|
|
|
// When the guesser bails out, the classic provider should be used
|
|
setupStubs();
|
|
guessedThumbnailInfoStub.returns( $.Deferred().reject() );
|
|
thumbnailInfoStub.returns( $.Deferred().resolve( { url: 'apiURL' } ) );
|
|
imageStub.returns( $.Deferred().resolve( image ) );
|
|
promise = viewer.fetchThumbnail( file, width, sampleURL, originalWidth, originalHeight );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( guessedThumbnailInfoStub.calledOnce, true, 'When the guesser bails out, GuessedThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( thumbnailInfoStub.calledOnce, true, 'When the guesser bails out, ThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( imageStub.calledOnce, true, 'When the guesser bails out, ImageProvider is called once' );
|
|
assert.strictEqual( imageStub.calledWith( 'apiURL' ), true, 'When the guesser bails out, ImageProvider is called with the API url' );
|
|
assert.strictEqual( promise.state(), 'resolved', 'When the guesser bails out, fetchThumbnail resolves' );
|
|
|
|
// When the guesser returns an URL, that should be used
|
|
setupStubs();
|
|
guessedThumbnailInfoStub.returns( $.Deferred().resolve( { url: 'guessedURL' } ) );
|
|
thumbnailInfoStub.returns( $.Deferred().resolve( { url: 'apiURL' } ) );
|
|
imageStub.returns( $.Deferred().resolve( image ) );
|
|
promise = viewer.fetchThumbnail( file, width, sampleURL, originalWidth, originalHeight );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( guessedThumbnailInfoStub.calledOnce, true, 'When the guesser returns an URL, GuessedThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( thumbnailInfoStub.called, false, 'When the guesser returns an URL, ThumbnailInfoProvider is not called' );
|
|
assert.strictEqual( imageStub.calledOnce, true, 'When the guesser returns an URL, ImageProvider is called once' );
|
|
assert.strictEqual( imageStub.calledWith( 'guessedURL' ), true, 'When the guesser returns an URL, ImageProvider is called with the guessed url' );
|
|
assert.strictEqual( promise.state(), 'resolved', 'When the guesser returns an URL, fetchThumbnail resolves' );
|
|
|
|
// When the guesser returns an URL, but that returns 404, image loading should be retried with the classic provider
|
|
setupStubs();
|
|
guessedThumbnailInfoStub.returns( $.Deferred().resolve( { url: 'guessedURL' } ) );
|
|
thumbnailInfoStub.returns( $.Deferred().resolve( { url: 'apiURL' } ) );
|
|
imageStub.withArgs( 'guessedURL' ).returns( $.Deferred().reject() );
|
|
imageStub.withArgs( 'apiURL' ).returns( $.Deferred().resolve( image ) );
|
|
promise = viewer.fetchThumbnail( file, width, sampleURL, originalWidth, originalHeight );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( guessedThumbnailInfoStub.calledOnce, true, 'When the guesser returns an URL, but that returns 404, GuessedThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( thumbnailInfoStub.calledOnce, true, 'When the guesser returns an URL, but that returns 404, ThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( imageStub.calledTwice, true, 'When the guesser returns an URL, but that returns 404, ImageProvider is called twice' );
|
|
assert.strictEqual( imageStub.getCall( 0 ).calledWith( 'guessedURL' ), true, 'When the guesser returns an URL, but that returns 404, ImageProvider is called first with the guessed url' );
|
|
assert.strictEqual( imageStub.getCall( 1 ).calledWith( 'apiURL' ), true, 'When the guesser returns an URL, but that returns 404, ImageProvider is called second with the guessed url' );
|
|
assert.strictEqual( promise.state(), 'resolved', 'When the guesser returns an URL, but that returns 404, fetchThumbnail resolves' );
|
|
|
|
// When even the retry fails, fetchThumbnail() should reject
|
|
setupStubs();
|
|
guessedThumbnailInfoStub.returns( $.Deferred().resolve( { url: 'guessedURL' } ) );
|
|
thumbnailInfoStub.returns( $.Deferred().resolve( { url: 'apiURL' } ) );
|
|
imageStub.withArgs( 'guessedURL' ).returns( $.Deferred().reject() );
|
|
imageStub.withArgs( 'apiURL' ).returns( $.Deferred().reject() );
|
|
promise = viewer.fetchThumbnail( file, width, sampleURL, originalWidth, originalHeight );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( guessedThumbnailInfoStub.calledOnce, true, 'When even the retry fails, GuessedThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( thumbnailInfoStub.calledOnce, true, 'When even the retry fails, ThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( imageStub.calledTwice, true, 'When even the retry fails, ImageProvider is called twice' );
|
|
assert.strictEqual( imageStub.getCall( 0 ).calledWith( 'guessedURL' ), true, 'When even the retry fails, ImageProvider is called first with the guessed url' );
|
|
assert.strictEqual( imageStub.getCall( 1 ).calledWith( 'apiURL' ), true, 'When even the retry fails, ImageProvider is called second with the guessed url' );
|
|
assert.strictEqual( promise.state(), 'rejected', 'When even the retry fails, fetchThumbnail rejects' );
|
|
|
|
useThumbnailGuessing = false;
|
|
|
|
// When guessing is disabled, the classic provider is used
|
|
setupStubs();
|
|
guessedThumbnailInfoStub.returns( $.Deferred().resolve( { url: 'guessedURL' } ) );
|
|
thumbnailInfoStub.returns( $.Deferred().resolve( { url: 'apiURL' } ) );
|
|
imageStub.returns( $.Deferred().resolve( image ) );
|
|
promise = viewer.fetchThumbnail( file, width );
|
|
clock.tick( 10 );
|
|
assert.strictEqual( guessedThumbnailInfoStub.called, false, 'When guessing is disabled, GuessedThumbnailInfoProvider is not called' );
|
|
assert.strictEqual( thumbnailInfoStub.calledOnce, true, 'When guessing is disabled, ThumbnailInfoProvider is called once' );
|
|
assert.strictEqual( imageStub.calledOnce, true, 'When guessing is disabled, ImageProvider is called once' );
|
|
assert.strictEqual( imageStub.calledWith( 'apiURL' ), true, 'When guessing is disabled, ImageProvider is called with the API url' );
|
|
assert.strictEqual( promise.state(), 'resolved', 'When guessing is disabled, fetchThumbnail resolves' );
|
|
|
|
clock.restore();
|
|
} );
|
|
|
|
QUnit.test( 'document.title', function ( assert ) {
|
|
var viewer = getMultimediaViewer(),
|
|
bootstrap = new MultimediaViewerBootstrap(),
|
|
title = new mw.Title( 'File:This_should_show_up_in_document_title.png' ),
|
|
oldDocumentTitle = document.title;
|
|
|
|
this.sandbox.stub( mw.loader, 'using' ).returns( $.Deferred().resolve( viewer ) );
|
|
viewer.currentImageFileTitle = title;
|
|
bootstrap.setupEventHandlers();
|
|
viewer.setTitle();
|
|
|
|
assert.notStrictEqual( document.title.match( title.getNameText() ), null, 'File name is visible in title' );
|
|
|
|
viewer.close();
|
|
bootstrap.cleanupEventHandlers();
|
|
|
|
assert.strictEqual( document.title, oldDocumentTitle, 'Original title restored after viewer is closed' );
|
|
} );
|
|
}() );
|