diff --git a/resources/mmv/mmv.js b/resources/mmv/mmv.js index b31e89d56..9d8f93062 100755 --- a/resources/mmv/mmv.js +++ b/resources/mmv/mmv.js @@ -362,17 +362,11 @@ }; MMVP.updateControls = function () { - var ui = this.ui, - prevNextTop = ( ( ui.$imageWrapper.height() / 2 ) - 60 ) + 'px'; + var numImages = this.lightbox.images ? this.lightbox.images.length : 0, + showNextButton = this.lightbox.currentIndex < (numImages - 1), + showPreviousButton = this.lightbox.currentIndex > 0; - ui.$postDiv.css( 'top', ui.$imageWrapper.height() ); - - ui.$nextButton.add( ui.$prevButton ).css( { - top: prevNextTop - } ); - - ui.$nextButton.toggleClass( 'disabled', this.lightbox.currentIndex >= ( ( this.lightbox.images ? this.lightbox.images.length : 0 ) - 1 ) ); - ui.$prevButton.toggleClass( 'disabled', this.lightbox.currentIndex <= 0 ); + this.ui.updateControls( showNextButton, showPreviousButton ); }; MMVP.registerLogging = function () { @@ -921,9 +915,7 @@ MultiLightbox = window.MultiLightbox; lightboxHooks = window.lightboxHooks; - var viewer = new MultimediaViewer(); - - mw.mediaViewer = viewer; + mw.mediaViewer = new MultimediaViewer(); handleHash(); window.addEventListener( 'popstate', handleHash ); diff --git a/resources/mmv/mmv.lightboxinterface.js b/resources/mmv/mmv.lightboxinterface.js index 2dfd2ec17..5ff4fce4a 100644 --- a/resources/mmv/mmv.lightboxinterface.js +++ b/resources/mmv/mmv.lightboxinterface.js @@ -124,6 +124,11 @@ this.$postDiv.css( 'top', ( $( window ).height() - 83 ) + 'px' ); MLBInterface.prototype.attach.call( this, parentId ); + + // Buttons fading might not had been reset properly after a hard fullscreen exit + // This needs to happen after the parent attach() because the buttons need to be attached + // to the DOM for $.fn.stop() to work + this.stopButtonsFade(); }; LIP.unattach = function () { @@ -149,6 +154,11 @@ this.handleEvent( 'keydown', function( e ) { ui.keydown( e ); } ); + // mousemove generates a ton of events, which is why we throttle it + this.handleEvent( 'mousemove.lip', $.throttle( 250, function( e ) { + ui.mousemove( e ); + } ) ); + MLBInterface.prototype.load.call( this, image ); }; @@ -242,7 +252,7 @@ // and we aren't sure why, but it's not really necessary // with the new interface anyway - it's basically fullscreen // already! - this.$closeButton + this.$buttons = this.$closeButton .add( this.$fullscreenButton ) .add( this.$nextButton ) .add( this.$prevButton ) @@ -683,14 +693,27 @@ } ); }; - LIP.enterFullscreen = function () { - MLBInterface.prototype.enterFullscreen.call( this ); - this.viewer.resize( this ); - }; + LIP.fullscreenChange = function( e ) { + MLBInterface.prototype.fullscreenChange.call( this, e ); + + // Fullscreen change events can happen after unattach(), in which + // case we shouldn't do anything UI-related + if ( !this.currentlyAttached ) { + return; + } - LIP.exitFullscreen = function () { - MLBInterface.prototype.exitFullscreen.call( this ); this.viewer.resize( this ); + + if ( this.isFullscreen ) { + // When entering fullscreen without a mousemove, the browser + // still thinks that the cursor is where it was prior to entering + // fullscreen. I.e. on top of the fullscreen button + // Thus, we purposefully reset the saved position, so that + // the fade out really takes place (otherwise it's cancelled + // by updateControls which is called a few times when fullscreen opens) + this.mousePosition = { x: 0, y: 0 }; + this.fadeOutButtons(); + } }; /** @@ -731,5 +754,117 @@ } }; + /** + * @method + * Handles mousemove events on the document + */ + LIP.mousemove = function ( e ) { + if ( e ) { + // Saving the mouse position is useful whenever we need to + // run LIP.mousemove manually, such as when going to the next/prev + // element + this.mousePosition = { x: e.pageX, y: e.pageY}; + } + + this.revealButtonsAndFadeIfNeeded(); + }; + + /** + * @method + * Reveals all active buttons and schedule a fade out if needed + */ + LIP.revealButtonsAndFadeIfNeeded = function () { + // Only fullscreen mode sees its buttons fade out when not used + if ( !this.isFullscreen ) { + return; + } + + if ( this.buttonsFadeTimeout ) { + clearTimeout( this.buttonsFadeTimeout ); + } + + // Stop ongoing animations and make sure the buttons that need to be displayed are displayed + this.stopButtonsFade(); + + // this.mousePosition can be empty, for instance when we enter fullscreen and haven't + // recorded a real mousemove event yet + if ( !this.mousePosition + || !this.isAnyActiveButtonHovered( this.mousePosition.x, this.mousePosition.y ) ) { + this.fadeOutButtons(); + } + }; + + /** + * @method + * Fades out the active buttons + */ + LIP.fadeOutButtons = function () { + var ui = this; + + // We don't use animation chaining because delay() can't be stop()ed + this.buttonsFadeTimeout = setTimeout( function() { + ui.$buttons.not( '.disabled' ).animate( { opacity: 0 }, 1000 ); + }, 1500 ); + }; + + /** + * @method + * Stops the fading animation of the buttons and cancel any opacity value + */ + LIP.stopButtonsFade = function () { + this.$buttons + .stop( true ) + .css( 'opacity', '' ); + }; + + /** + * @method + * Checks if any active buttons are currently hovered, given a position + * @param {number} x The horizontal coordinate of the position + * @param {number} y The vertical coordinate of the position + * @return bool + */ + LIP.isAnyActiveButtonHovered = function ( x, y ) { + // We don't use mouseenter/mouseleave events because content is subject + // to change underneath the cursor, eg. when entering fullscreen or + // when going prev/next (the button can disappear when reaching ends) + var hovered = false; + + this.$buttons.not( '.disabled' ).each( function( idx, e ) { + var $e = $( e ), + offset = $e.offset(); + + if ( y >= offset.top + && y <= offset.top + $e.height() + && x >= offset.left + && x <= offset.left + $e.width() ) { + hovered = true; + } + } ); + + return hovered; + }; + + /** + * @method + * Updates the next and prev buttons + * @param bool showPrevButton Whether the prev button should be revealed or not + * @param bool showNextButton Whether the next button should be revealed or not + */ + LIP.updateControls = function ( showPrevButton, showNextButton ) { + var prevNextTop = ( ( this.$imageWrapper.height() / 2 ) - 60 ) + 'px'; + + this.$postDiv.css( 'top', this.$imageWrapper.height() ); + + this.$nextButton.add( this.$prevButton ).css( { + top: prevNextTop + } ); + + this.$nextButton.toggleClass( 'disabled', !showPrevButton ); + this.$prevButton.toggleClass( 'disabled', !showNextButton ); + + this.revealButtonsAndFadeIfNeeded(); + }; + mw.LightboxInterface = LightboxInterface; }( mediaWiki, jQuery, OO, window.LightboxInterface ) ); diff --git a/resources/multilightbox/lightboxinterface.js b/resources/multilightbox/lightboxinterface.js index 398a8b16b..614b5b59b 100644 --- a/resources/multilightbox/lightboxinterface.js +++ b/resources/multilightbox/lightboxinterface.js @@ -67,19 +67,6 @@ lbinterface.unattach(); } } ); - - $( document ).on( 'jq-fullscreen-change', function ( e ) { - lbinterface.isFullscreen = e.fullscreen; - - lightboxHooks.callAll( 'fullscreen', this, e.fullscreen ); - - if ( !lbinterface.fullscreenButtonJustPressed && !e.fullscreen ) { - // Close the interface all the way if the user pressed 'esc' - lbinterface.unattach(); - } else if ( lbinterface.fullscreenButtonJustPressed ) { - lbinterface.fullscreenButtonJustPressed = false; - } - } ); } LIP = LightboxInterface.prototype; @@ -106,15 +93,28 @@ * @param {string} [parentId] parent id where we want to attach the UI. Mainly for testing. */ LIP.attach = function ( parentId ) { + var lbinterface = this, + $parent; + // Re-appending the same content can have nasty side-effects // Such as the browser leaving fullscreen mode if the fullscreened element is part of it if ( this.currentlyAttached ) { return; } - var parent = $( parentId || document.body ); + $( document ).on( 'jq-fullscreen-change.lip', function( e ) { + lbinterface.fullscreenChange( e ); + } ); - parent + $parent = $( parentId || document.body ); + + // Clean up fullscreen data because hard-existing fullscreen might have left + // jquery.fullscreen unable to remove the class and attribute, since $main wasn't + // attached to the DOM anymore at the time the jq-fullscreen-change event triggered + this.$main.data( 'isFullscreened', false ).removeClass( 'jq-fullscreened' ); + this.isFullscreen = false; + + $parent .append( this.$wrapper, this.$overlay @@ -129,6 +129,8 @@ // TODO(aarcos): This is global and it breaks tests. lightboxHooks.callAll( 'closeInterface', this ); + $( document ).off( 'jq-fullscreen-change.lip' ); + this.$wrapper.detach(); this.$overlay.detach(); @@ -268,5 +270,18 @@ } }; + LIP.fullscreenChange = function ( e ) { + this.isFullscreen = e.fullscreen; + + lightboxHooks.callAll( 'fullscreen', e.element, e.fullscreen ); + + if ( !this.fullscreenButtonJustPressed && !e.fullscreen ) { + // Close the interface all the way if the user pressed 'esc' + this.unattach(); + } else if ( this.fullscreenButtonJustPressed ) { + this.fullscreenButtonJustPressed = false; + } + }; + window.LightboxInterface = LightboxInterface; }( jQuery ) ); diff --git a/tests/qunit/lightboxinterface.test.js b/tests/qunit/lightboxinterface.test.js index 296b7dcff..fb6d6c15a 100644 --- a/tests/qunit/lightboxinterface.test.js +++ b/tests/qunit/lightboxinterface.test.js @@ -1,7 +1,7 @@ ( function ( mw, $ ) { QUnit.module( 'multilightbox.interface', QUnit.newMwEnvironment() ); - QUnit.test( 'Sanity test, object creation and ui construction', 6, function ( assert ) { + QUnit.test( 'Sanity test, object creation and ui construction', 9, function ( assert ) { var lightbox = new window.LightboxInterface(); function checkIfUIAreasAttachedToDocument( inDocument ) { @@ -20,17 +20,10 @@ // UI areas should now be attached to the document. checkIfUIAreasAttachedToDocument(1); - /* - * TODO(aarcos): We cannot test the section below because unattach() - * depends on global lightboxHooks that expect a mw.LightboxInterface object. - * Fix once this dependency is resolved. - // Unattach lightbox from document - //lightbox.unattach(); + lightbox.unattach(); - // UI areas not attached to the document anymore. - //checkIfUIAreasAttachedToDocument(0); - */ + checkIfUIAreasAttachedToDocument(0); } ); QUnit.asyncTest( 'Check we are saving the resize listener', 2, function ( assert ) { @@ -56,7 +49,7 @@ lightbox.load(img); } ); - QUnit.test( 'Fullscreen mode', 4, function ( assert ) { + QUnit.test( 'Fullscreen mode', 6, function ( assert ) { var lightbox = new window.LightboxInterface(), oldFnEnterFullscreen = $.fn.enterFullscreen, oldFnExitFullscreen = $.fn.exitFullscreen; @@ -84,6 +77,24 @@ 'Fullscreened area doesn\'t have the fullscreen class anymore'); assert.strictEqual( lightbox.isFullscreen , false, 'Lightbox knows it\'s not in fullscreen mode'); + // Entering fullscreen + lightbox.$fullscreenButton.click(); + + // Hard-exiting fullscreen + lightbox.$closeButton.click(); + + // Re-attach after hard-exit + lightbox.attach( '#qunit-fixture' ); + + assert.strictEqual( lightbox.$main.hasClass( 'jq-fullscreened' ) , false, + 'Fullscreened area doesn\'t have the fullscreen class anymore'); + assert.strictEqual( lightbox.isFullscreen , false, 'Lightbox knows it\'s not in fullscreen mode'); + + // Unattach lightbox from document + lightbox.unattach(); + + + $.fn.enterFullscreen = oldFnEnterFullscreen; $.fn.exitFullscreen = oldFnExitFullscreen; } ); diff --git a/tests/qunit/mmv.lightboxinterface.test.js b/tests/qunit/mmv.lightboxinterface.test.js index a8a7fc3ad..04bb342ff 100644 --- a/tests/qunit/mmv.lightboxinterface.test.js +++ b/tests/qunit/mmv.lightboxinterface.test.js @@ -44,7 +44,7 @@ // Check that the close button on the lightbox still follow the spec (being visible right away) assert.strictEqual( $( '#qunit-fixture .mlb-close' ).length, 1, 'There should be a close button' ); - assert.strictEqual( $( '#qunit-fixture .mlb-close' ).is(':visible'), true, 'The close button should be visible' ); + assert.ok( $( '#qunit-fixture .mlb-close' ).is(':visible'), 'The close button should be visible' ); // Unattach lightbox from document lightbox.unattach(); @@ -64,10 +64,10 @@ } for ( i = 0; i < thingsShouldHaveEmptyClass.length; i++ ) { - assert.strictEqual( lightbox[thingsShouldHaveEmptyClass[i]].hasClass( 'empty' ), true, 'We successfully applied the empty class to the ' + thingsShouldHaveEmptyClass[i] + ' element' ); + assert.ok( lightbox[thingsShouldHaveEmptyClass[i]].hasClass( 'empty' ), 'We successfully applied the empty class to the ' + thingsShouldHaveEmptyClass[i] + ' element' ); } - assert.strictEqual( lightbox.$dragIcon.hasClass( 'pointing-down' ), false, 'We successfully reset the chevron' ); + assert.ok( !lightbox.$dragIcon.hasClass( 'pointing-down' ), 'We successfully reset the chevron' ); } ); QUnit.test( 'Handler registration and clearance work OK', 2, function ( assert ) { @@ -121,10 +121,12 @@ ); } ); - QUnit.test( 'Fullscreen mode', 3, function ( assert ) { + QUnit.test( 'Fullscreen mode', 8, function ( assert ) { var lightbox = new mw.LightboxInterface( mw.mediaViewer ), oldFnEnterFullscreen = $.fn.enterFullscreen, - oldFnExitFullscreen = $.fn.exitFullscreen; + oldFnExitFullscreen = $.fn.exitFullscreen, + oldRevealButtonsAndFadeIfNeeded, + buttonOffset; // Since we don't want these tests to really open fullscreen // which is subject to user security confirmation, @@ -137,17 +139,43 @@ lightbox.viewer.ui = lightbox; lightbox.viewer.lightbox = lightbox; - assert.strictEqual( lightbox.$imageMetadata.is( ':visible' ) , true, 'Image metadata is visible' ); + assert.ok( !lightbox.isFullscreen, 'Lightbox knows that it\'s not in fullscreen mode' ); + assert.ok( lightbox.$imageMetadata.is( ':visible' ), 'Image metadata is visible' ); - // Entering fullscreen + lightbox.fadeOutButtons = function() { + assert.ok( true, 'Opening fullscreen triggers a fadeout' ); + }; + + // Pretend that the mouse cursor is on top of the button + buttonOffset = lightbox.$fullscreenButton.offset(); + lightbox.mousePosition = { x: buttonOffset.left, y: buttonOffset.top }; + + // Enter fullscreen lightbox.$fullscreenButton.click(); - assert.strictEqual( lightbox.$imageMetadata.is( ':visible' ) , false, 'Image metadata is hidden' ); + lightbox.fadeOutButtons = $.noop; + assert.ok( lightbox.isFullscreen, 'Lightbox knows that it\'s in fullscreen mode' ); - // Exiting fullscreen + oldRevealButtonsAndFadeIfNeeded = lightbox.revealButtonsAndFadeIfNeeded; + + lightbox.revealButtonsAndFadeIfNeeded = function() { + assert.ok( true, 'Moving the cursor triggers a reveal + fade' ); + + oldRevealButtonsAndFadeIfNeeded.call( this ); + }; + + // Pretend that the mouse cursor moved to the top-left corner + lightbox.mousemove( { pageX: 0, pageY: 0 } ); + + lightbox.revealButtonsAndFadeIfNeeded = $.noop; + + assert.ok( !lightbox.$imageMetadata.is( ':visible' ), 'Image metadata is hidden' ); + + // Exit fullscreen lightbox.$fullscreenButton.click(); - assert.strictEqual( lightbox.$imageMetadata.is( ':visible' ) , true, 'Image metadata is visible' ); + assert.ok( lightbox.$imageMetadata.is( ':visible' ), 'Image metadata is visible' ); + assert.ok( !lightbox.isFullscreen, 'Lightbox knows that it\'s not in fullscreen mode' ); // Unattach lightbox from document lightbox.unattach(); @@ -156,6 +184,41 @@ $.fn.exitFullscreen = oldFnExitFullscreen; } ); + QUnit.test( 'isAnyActiveButtonHovered', 20, function ( assert ) { + var lightbox = new mw.LightboxInterface( mw.mediaViewer ); + + // Attach lightbox to testing fixture to avoid interference with other tests. + lightbox.attach( '#qunit-fixture' ); + + $.each ( lightbox.$buttons, function ( idx, e ) { + var $e = $( e ), + offset = $e.offset(), + width = $e.width(), + height = $e.height(), + disabled = $e.hasClass( 'disabled' ); + + assert.strictEqual( lightbox.isAnyActiveButtonHovered( offset.left, offset.top ), + !disabled, + 'Hover detection works for top-left corner of element' ); + assert.strictEqual( lightbox.isAnyActiveButtonHovered( offset.left + width, offset.top ), + !disabled, + 'Hover detection works for top-right corner of element' ); + assert.strictEqual( lightbox.isAnyActiveButtonHovered( offset.left, offset.top + height ), + !disabled, + 'Hover detection works for bottom-left corner of element' ); + assert.strictEqual( lightbox.isAnyActiveButtonHovered( offset.left + width, offset.top + height ), + !disabled, + 'Hover detection works for bottom-right corner of element' ); + assert.strictEqual( lightbox.isAnyActiveButtonHovered( + offset.left + ( width / 2 ), offset.top + ( height / 2 ) ), + !disabled, + 'Hover detection works for center of element' ); + } ); + + // Unattach lightbox from document + lightbox.unattach(); + } ); + QUnit.test( 'Metadata scrolling', 13, function ( assert ) { var lightbox = new mw.LightboxInterface( mw.mediaViewer ), keydown = $.Event( 'keydown' ), @@ -219,15 +282,15 @@ lightbox.load( { getImageElement: function() { return $.Deferred().reject(); } } ); assert.strictEqual( $.scrollTo().scrollTop(), 0, 'scrollTo scrollTop should be set to 0' ); - assert.strictEqual( lightbox.$dragIcon.hasClass( 'pointing-down' ), false, + assert.ok( !lightbox.$dragIcon.hasClass( 'pointing-down' ), 'Chevron pointing up' ); keydown.which = 38; // Up arrow $document.trigger( keydown ); - assert.strictEqual( $.scrollTo().scrollTop(), lightbox.$imageMetadata.height() + 1, + assert.strictEqual( Math.round( $.scrollTo().scrollTop() ), lightbox.$imageMetadata.height() + 1, 'scrollTo scrollTop should be set to the metadata height + 1 after pressing up arrow' ); - assert.strictEqual( lightbox.$dragIcon.hasClass( 'pointing-down' ), true, + assert.ok( lightbox.$dragIcon.hasClass( 'pointing-down' ), 'Chevron pointing down after pressing up arrow' ); keydown.which = 40; // Down arrow @@ -235,21 +298,21 @@ assert.strictEqual( $.scrollTo().scrollTop(), 0, 'scrollTo scrollTop should be set to 0 after pressing down arrow' ); - assert.strictEqual( lightbox.$dragIcon.hasClass( 'pointing-down' ), false, + assert.ok( !lightbox.$dragIcon.hasClass( 'pointing-down' ), 'Chevron pointing up after pressing down arrow' ); lightbox.$dragIcon.click(); - assert.strictEqual( $.scrollTo().scrollTop(), lightbox.$imageMetadata.height() + 1, + assert.strictEqual( Math.round( $.scrollTo().scrollTop() ), lightbox.$imageMetadata.height() + 1, 'scrollTo scrollTop should be set to the metadata height + 1 after clicking the chevron once' ); - assert.strictEqual( lightbox.$dragIcon.hasClass( 'pointing-down' ), true, + assert.ok( lightbox.$dragIcon.hasClass( 'pointing-down' ), 'Chevron pointing down after clicking the chevron once' ); lightbox.$dragIcon.click(); assert.strictEqual( $.scrollTo().scrollTop(), 0, 'scrollTo scrollTop should be set to 0 after clicking the chevron twice' ); - assert.strictEqual( lightbox.$dragIcon.hasClass( 'pointing-down' ), false, + assert.ok( !lightbox.$dragIcon.hasClass( 'pointing-down' ), 'Chevron pointing up after clicking the chevron twice' ); // Unattach lightbox from document diff --git a/tests/qunit/mmv.test.js b/tests/qunit/mmv.test.js index 7e73e942a..2514ae988 100644 --- a/tests/qunit/mmv.test.js +++ b/tests/qunit/mmv.test.js @@ -76,6 +76,9 @@ link3.trigger( 'click' ); assert.notStrictEqual( viewer.lightbox, null, 'There are legit links, a lightbox should be created.' ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Skip images with invalid extensions', 1, function ( assert ) { @@ -97,6 +100,9 @@ link.trigger( 'click' ); assert.strictEqual( viewer.lightbox, null, 'There are not legit links, a lightbox should not be created.' ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Accept only left clicks without modifier keys, skip the rest', 1, function ( assert ) { @@ -132,6 +138,9 @@ // Skip invalid right click, no image is loaded link.trigger( rightClick ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Do not load the resized image if no data returning from the api', 1, function ( assert ) { @@ -143,6 +152,9 @@ viewer.loadResizedImage( ui, data ); assert.ok( true, 'Resized image is not replaced since we have not data.' ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Logging works as expected', 4 * logTests.length, function ( assert ) { @@ -174,6 +186,9 @@ mw.log = backupLog; mw.eventLog = backupEventLog; + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Profiling works as expected', ( 12 * profileTests.length ), function ( assert ) { @@ -221,6 +236,9 @@ mw.log = backupLog; mw.eventLog = backupEventLog; + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.asyncTest( 'Make sure we get sane values for the eventlogging timing', 2, function ( assert ) { @@ -240,6 +258,9 @@ } else { mw.eventLog = backupEventLog; QUnit.start(); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } } }; @@ -259,6 +280,9 @@ }; link.trigger( 'click' ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Validate new LightboxImage object has sane constructor parameters', 6, function ( assert ) { @@ -281,6 +305,9 @@ viewer = new mw.MultimediaViewer(); mw.MultimediaViewer.prototype.createNewImage = backup; + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Validate new LightboxImage object has sane constructor parameters', 6, function ( assert ) { @@ -303,6 +330,9 @@ viewer = new mw.MultimediaViewer(); mw.MultimediaViewer.prototype.createNewImage = backup; + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'We get sane image sizes when we ask for them', 5, function ( assert ) { @@ -313,6 +343,9 @@ assert.strictEqual( viewer.findNextHighestImageSize( 320.00001 ), 640, 'Asking for greater than an image bucket definitely gives us the next size up' ); assert.strictEqual( viewer.findNextHighestImageSize( 2000 ), 2560, 'The image bucketing also works on big screens' ); assert.strictEqual( viewer.findNextHighestImageSize( 3000 ), 2880, 'The image bucketing also works on REALLY big screens' ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Metadata div is only animated once', 4, function ( assert ) { @@ -335,6 +368,9 @@ assert.strictEqual( animationRan, false, 'The second call to animateMetadataDivOnce did not lead to an animation' ); $.fn.animate = backupAnimation; + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'HTML whitelisting works', 2, function ( assert ) { @@ -351,6 +387,9 @@ viewer.whitelistHtml( $sandbox.empty().append( nwljq ) ); assert.strictEqual( $sandbox.html(), whitelisted, 'Not-whitelisted elements are removed.' ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'The location data is set correctly', 11, function ( assert ) { @@ -386,6 +425,9 @@ }; viewer.setLocationData( imageData ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'The location data is set correctly with weird values', 11, function ( assert ) { @@ -421,6 +463,9 @@ }; viewer.setLocationData( imageData ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'The location data is not set if no value is passed in', 1, function ( assert ) { @@ -443,6 +488,9 @@ viewer.setLocationData( imageData ); assert.strictEqual( called, false, 'The interface data-setter method is not called if there are no coordinates available for the image.' ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'getImageSizeApiArgs(): Limited by height and limited by width', 4, function ( assert ) { @@ -479,6 +527,9 @@ assert.strictEqual( widths.requested, 640 * $.devicePixelRatio(), 'Correct requested width was computed.' ); ui.unattach(); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.asyncTest( 'loadAndSetImage(): Basic load', 9, function ( assert ) { @@ -528,6 +579,9 @@ requestedWidth = 640; profileEvent = 'image-load'; viewer.loadAndSetImage( ui, imageData, targetWidth, requestedWidth, profileEvent ); + + // Clean up the viewer, to avoid seeing it catch events when running other tests + mw.mmvTestHelpers.resetViewer(); } ); QUnit.test( 'Hash handling', 5, function ( assert ) { diff --git a/tests/qunit/mmv.testhelpers.js b/tests/qunit/mmv.testhelpers.js index 4d777105e..08ffe4cbd 100644 --- a/tests/qunit/mmv.testhelpers.js +++ b/tests/qunit/mmv.testhelpers.js @@ -13,5 +13,12 @@ $( document ).trigger( $.Event( 'jq-fullscreen-change', { element: this, fullscreen: false } ) ); }; + // TODO: remove once viewer isn't being referenced by interfaces anymore + // and event listening code has been cleaned up + MTH.resetViewer = function() { + lightboxHooks.constructor(); + mw.mediaViewer.constructor(); + }; + mw.mmvTestHelpers = MTH; } )( mediaWiki, jQuery ); \ No newline at end of file