mediawiki-extensions-Popups/resources/ext.popups/checkin.js
joakin 84043e86f9 Fix checkin tests to use fake timers
* To speed the time the tests spend running
* To make them reliably execute and not depend on magic numbers

Additional changes:
* Use mw.now instead of Date on checkin.js
* Split big setVisibleTimeout test into several smaller ones
  * Extract helpers for the tests

Change-Id: I9d3233fccf6de0997f968d096e375df996e87786
2017-01-10 09:31:32 -08:00

131 lines
3.8 KiB
JavaScript

( function ( mw, $ ) {
var pageVisibility = mw.popups.pageVisibility,
checkin = {
/**
* Checkin times - Fibonacci numbers
*
* Exposed for testing only.
*
* @type {number[]}
* @private
*/
CHECKIN_TIMES: [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,
377, 610, 987, 1597, 2584, 4181, 6765 ],
/**
* Have checkin actions been setup already?
*
* Exposed for testing only.
*
* @private
* @type {boolean}
*/
haveCheckinActionsBeenSetup: false
};
/**
* A customized `setTimeout` function that takes page visibility into account
*
* If the document is not visible to the user, e.g. browser window is minimized,
* then pause the time. Otherwise execute `callback` after `delay` milliseconds.
* The callback won't be executed if the browser does not suppor the page visibility
* API.
*
* Exposed for testing only.
*
* @see https://www.w3.org/TR/page-visibility/#dom-document-hidden
* @private
* @param {Function} callback Function to call when the time is up
* @param {number} delay The number of milliseconds to wait before executing the callback
*/
checkin.setVisibleTimeout = function ( callback, delay ) {
var hiddenPropertyName = pageVisibility.getDocumentHiddenPropertyName( document ),
visibilityChangeEventName = pageVisibility.getDocumentVisibilitychangeEventName( document ),
timeoutId,
lastStartedAt;
if ( !hiddenPropertyName || !visibilityChangeEventName ) {
return;
}
/**
* Execute the callback and turn off listening to the visibilitychange event
*/
function done() {
callback();
$( document ).off( visibilityChangeEventName, visibilityChangeHandler );
}
/**
* Pause or resume the timer depending on the page visibility state
*/
function visibilityChangeHandler() {
var millisecondsPassed;
// Pause the timer if the page is hidden ...
if ( pageVisibility.isDocumentHidden( document ) ) {
// ... and only if the timer has started.
// Timer may not have been started if the document opened in a
// hidden tab for example. The timer will be started when the
// document is visible to the user.
if ( lastStartedAt !== undefined ) {
millisecondsPassed = Math.round( mw.now() - lastStartedAt );
delay = Math.max( 0, delay - millisecondsPassed );
clearTimeout( timeoutId );
}
} else {
lastStartedAt = Math.round( mw.now() );
timeoutId = setTimeout( done, delay );
}
}
visibilityChangeHandler();
$( document ).on( visibilityChangeEventName, visibilityChangeHandler );
};
/**
* Perform the passed `checkin` action at the predefined times
*
* Actions are setup only once no matter how many times this function is
* called. Ideally this function should be called once.
*
* @see checkin.CHECKIN_TIMES
* @param {Function} checkinAction
*/
checkin.setupActions = function( checkinAction ) {
var timeIndex = 0,
timesLength = checkin.CHECKIN_TIMES.length,
time, // current checkin time
nextTime; // the checkin time that will be logged next
if ( checkin.haveCheckinActionsBeenSetup ) {
return;
}
/**
* Execute the checkin action with the current checkin time
*
* If more checkin times are left, then setup a timer to log the next one.
*/
function setup() {
time = checkin.CHECKIN_TIMES[ timeIndex ];
checkinAction( time );
timeIndex += 1;
if ( timeIndex < timesLength ) {
nextTime = checkin.CHECKIN_TIMES[ timeIndex ];
// Execute the callback after the number of seconds left till the
// next checkin time.
checkin.setVisibleTimeout( setup, ( nextTime - time ) * 1000 );
}
}
checkin.setVisibleTimeout( setup, checkin.CHECKIN_TIMES[ timeIndex ] * 1000 );
checkin.haveCheckinActionsBeenSetup = true;
};
mw.popups.checkin = checkin;
}( mediaWiki, jQuery ) );