' )
.attr( 'id', 'file' )
.appendTo( '#qunit-fixture' )
)
);
// Create a new bootstrap object to trigger the DOM scan, etc.
const bootstrap = createBootstrap( viewer );
this.sandbox.stub( bootstrap, 'setupOverlay' );
const $link4 = $( '.fullMedia .mw-mmv-view-expanded' );
assert.ok( $link4.length, 'Link for viewing expanded file was set up.' );
const $link5 = $( '.fullMedia .mw-mmv-view-config' );
assert.ok( $link5.length, 'Link for opening enable/disable configuration was set up.' );
// Click on valid link
$link.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
// FIXME: Actual bootstrap.setupOverlay.callCount: 2
assert.equal( bootstrap.setupOverlay.callCount, 1, 'setupOverlay called (1st click)' );
assert.equal( viewer.loadImageByTitle.callCount, 1, 'loadImageByTitle called (1st click)' );
this.sandbox.reset();
// Click on valid link
$link2.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
assert.equal( bootstrap.setupOverlay.callCount, 1, 'setupOverlay called (2nd click)' );
assert.equal( viewer.loadImageByTitle.callCount, 1, 'loadImageByTitle called (2nd click)' );
this.sandbox.reset();
// Click on valid link
$link4.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
assert.equal( bootstrap.setupOverlay.callCount, 1, 'setupOverlay called (3rd click)' );
assert.equal( viewer.loadImageByTitle.callCount, 1, 'loadImageByTitle called (3rd click)' );
this.sandbox.reset();
// Click on valid link even when preference says not to
mw.config.set( 'wgMediaViewerOnClick', false );
$link4.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
mw.config.set( 'wgMediaViewerOnClick', true );
assert.equal( bootstrap.setupOverlay.callCount, 1, 'setupOverlay called on-click with pref off' );
assert.equal( viewer.loadImageByTitle.callCount, 1, 'loadImageByTitle called on-click with pref off' );
this.sandbox.reset();
// @todo comment that above clicks should result in call, below clicks should not
// Click on non-valid link
$link3.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
assert.equal( bootstrap.setupOverlay.callCount, 0, 'setupOverlay not called on non-valid link click' );
assert.equal( viewer.loadImageByTitle.callCount, 0, 'loadImageByTitle not called on non-valid link click' );
this.sandbox.reset();
// Click on valid links with preference off
mw.config.set( 'wgMediaViewerOnClick', false );
$link.trigger( { type: 'click', which: 1 } );
$link2.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
assert.equal( bootstrap.setupOverlay.callCount, 0, 'setupOverlay not called on non-valid link click with pref off' );
assert.equal( viewer.loadImageByTitle.callCount, 0, 'loadImageByTitle not called on non-valid link click with pref off' );
clock.restore();
} );
QUnit.test( 'Skip images with invalid extensions', function ( assert ) {
const viewer = { initWithThumbs: function () {}, loadImageByTitle: this.sandbox.stub() };
const clock = this.sandbox.useFakeTimers();
// Create gallery with image that has invalid name extension
const div = createGallery( 'thumb.badext' );
const link = div.find( 'a.image' );
// Create a new bootstrap object to trigger the DOM scan, etc.
createBootstrap( viewer );
// Click on valid link with wrong image extension
link.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
assert.strictEqual( viewer.loadImageByTitle.called, false, 'Image should not be loaded' );
clock.restore();
} );
// FIXME: Tests suspended as they do not pass in QUnit 2.x+ – T192932
QUnit.skip( 'Accept only left clicks without modifier keys, skip the rest', function ( assert ) {
const viewer = { initWithThumbs: function () {}, loadImageByTitle: this.sandbox.stub() };
const clock = this.sandbox.useFakeTimers();
// Create gallery with image that has valid name extension
const $div = createGallery();
// Create a new bootstrap object to trigger the DOM scan, etc.
const bootstrap = createBootstrap( viewer );
this.sandbox.stub( bootstrap, 'setupOverlay' );
const $link = $div.find( 'a.image' );
// Handle valid left click, it should try to load the image
$link.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
// FIXME: Actual bootstrap.setupOverlay.callCount: 2
assert.equal( bootstrap.setupOverlay.callCount, 1, 'Left-click: Set up overlay' );
assert.equal( viewer.loadImageByTitle.callCount, 1, 'Left-click: Load image' );
this.sandbox.reset();
// Skip Ctrl-left-click, no image is loaded
$link.trigger( { type: 'click', which: 1, ctrlKey: true } );
clock.tick( 10 );
assert.equal( bootstrap.setupOverlay.callCount, 0, 'Ctrl-left-click: No overlay' );
assert.equal( viewer.loadImageByTitle.callCount, 0, 'Ctrl-left-click: No image load' );
this.sandbox.reset();
// Skip invalid right click, no image is loaded
$link.trigger( { type: 'click', which: 2 } );
clock.tick( 10 );
assert.equal( bootstrap.setupOverlay.callCount, 0, 'Right-click: No overlay' );
assert.equal( viewer.loadImageByTitle.callCount, 0, 'Right-click: Image was not loaded' );
clock.restore();
} );
QUnit.test( 'Ensure that the correct title is loaded when clicking', function ( assert ) {
const viewer = { initWithThumbs: function () {}, loadImageByTitle: this.sandbox.stub() };
const clock = this.sandbox.useFakeTimers();
// Create a new bootstrap object to trigger the DOM scan, etc.
const bootstrap = createBootstrap( viewer );
this.sandbox.stub( bootstrap, 'setupOverlay' );
bootstrap.route( 'File:Foo.jpg' );
clock.tick( 10 );
assert.true( bootstrap.setupOverlay.called, 'Overlay was set up' );
assert.strictEqual( viewer.loadImageByTitle.firstCall.args[ 0 ].getPrefixedDb(), 'File:Foo.jpg', 'Titles are identical' );
clock.tick( 10 );
clock.restore();
} );
// FIXME: Tests suspended as they do not pass in QUnit 2.x+ – T192932
QUnit.skip( 'Validate new LightboxImage object has sensible constructor parameters', function ( assert ) {
const viewer = getMultimediaViewer();
const fname = 'valid';
const imgSrc = '/' + fname + '.jpg/300px-' + fname + '.jpg';
const imgRegex = new RegExp( imgSrc + '$' );
const clock = this.sandbox.useFakeTimers();
const $div = createThumb( imgSrc, 'Blah blah', 'meow' );
const $link = $div.find( 'a.image' );
// Create a new bootstrap object to trigger the DOM scan, etc.
const bootstrap = createBootstrap( viewer );
this.sandbox.stub( bootstrap, 'setupOverlay' );
this.sandbox.stub( viewer, 'createNewImage' );
viewer.loadImage = function () {};
viewer.createNewImage = function ( fileLink, filePageLink, fileTitle, index, thumb, caption, alt ) {
const html = thumb.outerHTML;
// FIXME: fileLink doesn't match imgRegex (null)
assert.ok( fileLink.match( imgRegex ), 'Thumbnail URL used in creating new image object' );
assert.strictEqual( filePageLink, '', 'File page link is sensible when creating new image object' );
assert.strictEqual( fileTitle.title, fname, 'Filename is correct when passed into new image constructor' );
assert.strictEqual( index, 0, 'The only image we created in the gallery is set at index 0 in the images array' );
assert.ok( html.indexOf( ' src="' + imgSrc + '"' ) > 0, 'The image element passed in contains the src=... we want.' );
assert.ok( html.indexOf( ' alt="meow"' ) > 0, 'The image element passed in contains the alt=... we want.' );
assert.strictEqual( caption, 'Blah blah', 'The caption passed in is correct' );
assert.strictEqual( alt, 'meow', 'The alt text passed in is correct' );
};
$link.trigger( { type: 'click', which: 1 } );
clock.tick( 10 );
assert.equal( bootstrap.setupOverlay.callCount, 1, 'Overlay was set up' );
clock.reset();
} );
QUnit.test( 'Only load the viewer on a valid hash', function ( assert ) {
location.hash = '';
const bootstrap = createBootstrap();
return hashTest( '/media', bootstrap, assert );
} );
QUnit.test( 'Load the viewer on a legacy hash', function ( assert ) {
location.hash = '';
const bootstrap = createBootstrap();
return hashTest( 'mediaviewer', bootstrap, assert );
} );
QUnit.test( 'Overlay is set up on hash change', function ( assert ) {
location.hash = '#/media/foo';
const bootstrap = createBootstrap();
this.sandbox.stub( bootstrap, 'setupOverlay' );
bootstrap.hash();
assert.true( bootstrap.setupOverlay.called, 'Overlay is set up' );
} );
QUnit.test( 'Overlay is not set up on an irrelevant hash change', function ( assert ) {
location.hash = '#foo';
const bootstrap = createBootstrap();
this.sandbox.stub( bootstrap, 'setupOverlay' );
bootstrap.loadViewer();
bootstrap.setupOverlay.reset();
bootstrap.hash();
assert.strictEqual( bootstrap.setupOverlay.called, false, 'Overlay is not set up' );
} );
QUnit.test( 'Restoring article scroll position', function ( assert ) {
let stubbedScrollTop;
const bootstrap = createBootstrap();
const $window = $( window );
const done = assert.async();
this.sandbox.stub( $.fn, 'scrollTop', function ( scrollTop ) {
if ( scrollTop !== undefined ) {
stubbedScrollTop = scrollTop;
return this;
} else {
return stubbedScrollTop;
}
} );
$window.scrollTop( 50 );
bootstrap.setupOverlay();
// Calling this a second time because it can happen in history navigation context
bootstrap.setupOverlay();
// Clear scrollTop to check it is restored
$window.scrollTop( 0 );
bootstrap.cleanupOverlay();
// Scroll restoration is on a setTimeout
setTimeout( function () {
assert.strictEqual( $( window ).scrollTop(), 50, 'Scroll is correctly reset to original top position' );
done();
} );
} );
QUnit.test( 'Preload JS/CSS dependencies on thumb hover', function ( assert ) {
const clock = this.sandbox.useFakeTimers();
const viewer = { initWithThumbs: function () {} };
// Create gallery with image that has valid name extension
const $div = createThumb();
// Create a new bootstrap object to trigger the DOM scan, etc.
const bootstrap = createBootstrap( viewer );
this.sandbox.stub( mw.loader, 'load' );
$div.trigger( 'mouseenter' );
clock.tick( bootstrap.hoverWaitDuration - 50 );
$div.trigger( 'mouseleave' );
assert.strictEqual( mw.loader.load.called, false, 'Dependencies should not be preloaded if the thumb is not hovered long enough' );
$div.trigger( 'mouseenter' );
clock.tick( bootstrap.hoverWaitDuration + 50 );
$div.trigger( 'mouseleave' );
assert.strictEqual( mw.loader.load.called, true, 'Dependencies should be preloaded if the thumb is hovered long enough' );
clock.restore();
} );
QUnit.test( 'isAllowedThumb', function ( assert ) {
const $container = $( '
' );
const $thumb = $( '
' ).appendTo( $container );
const bootstrap = createBootstrap();
assert.strictEqual( bootstrap.isAllowedThumb( $thumb ), true, 'Normal image in a div is allowed.' );
$container.addClass( 'metadata' );
assert.strictEqual( bootstrap.isAllowedThumb( $thumb ), false, 'Image in a metadata container is disallowed.' );
$container.removeClass().addClass( 'noviewer' );
assert.strictEqual( bootstrap.isAllowedThumb( $thumb ), false, 'Image in a noviewer container is disallowed.' );
$container.removeClass().addClass( 'noarticletext' );
assert.strictEqual( bootstrap.isAllowedThumb( $thumb ), false, 'Image in an empty article is disallowed.' );
$container.removeClass().addClass( 'noviewer' );
assert.strictEqual( bootstrap.isAllowedThumb( $thumb ), false, 'Image with a noviewer class is disallowed.' );
} );
QUnit.test( 'findCaption', function ( assert ) {
const gallery = createGallery( 'foo.jpg', 'Baz' );
const thumb = createThumb( 'foo.jpg', 'Quuuuux' );
const link = createNormal( 'foo.jpg', 'Foobar' );
const multiple = createMultipleImage( [
[ 'foo.jpg', 'Image #1' ],
[ 'bar.jpg', 'Image #2' ],
[ 'foobar.jpg', 'Image #3' ]
] );
const bootstrap = createBootstrap();
assert.strictEqual( bootstrap.findCaption( gallery.find( '.thumb' ), gallery.find( 'a.image' ) ), 'Baz', 'A gallery caption is found.' );
assert.strictEqual( bootstrap.findCaption( thumb, thumb.find( 'a.image' ) ), 'Quuuuux', 'A thumbnail caption is found.' );
assert.strictEqual( bootstrap.findCaption( $(), link ), 'Foobar', 'The caption is found even if the image is not a thumbnail.' );
assert.strictEqual( bootstrap.findCaption( multiple, multiple.find( 'img[src="bar.jpg"]' ).closest( 'a' ) ), 'Image #2', 'The caption is found in {{Multiple image}}.' );
} );
}() );