mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Popups
synced 2024-12-26 05:52:57 +00:00
efb7b3d872
The save action has been implemented, and it is listened to by the canonical enabled state in the previews reducer, and by the settings reducer to perform UI changes. The enabled state of the application has been kept in the preview reducer as the canonical source of truth. See supporting changes for documentation about the decision. Actions: * Introduce new action SETTINGS_CHANGE with the enabled status * Trigger that action when clicking Save in the settings dialog Reducers: * Listen to SETTINGS_CHANGE in the preview reducer to update enabled status * On the settings reducer * Handle the SETTINGS_CHANGE action * Add showHelp flag to determine if the help should be showing Change listeners: * Switch to compare past vs present changes in the implementation * Handle showing and hiding the help Supporting changes: * On the rendered settings dialog: * Change #hide to actually just hide and remove legacy if statement * Add #toggleHelp method to show or hide the help dialog * Add doc/adr/0003-keep-enabled-state-only-in-preview-reducer.md to support the decision of making the saveSettings action creator return a Redux.Thunk and keeping the enabled state just in the preview reducer. * Add NB to actions#saveSettings explaining and linking to the document Follow commits soon: * Persist the settings change to local storage when it changes, and unify with the preview change listener Change-Id: I80dc5f29fbe6286f2e3e3b50d909894bc5041ccd
292 lines
5.7 KiB
JavaScript
292 lines
5.7 KiB
JavaScript
( function ( mw, $ ) {
|
|
|
|
function generateToken() {
|
|
return '9876543210';
|
|
}
|
|
|
|
QUnit.module( 'ext.popups/actions' );
|
|
|
|
QUnit.test( '#boot', function ( assert ) {
|
|
var isUserInCondition = function () {
|
|
return false;
|
|
},
|
|
config = new mw.Map(),
|
|
stubUser = mw.popups.tests.stubs.createStubUser( /* isAnon = */ true ),
|
|
stubUserSettings,
|
|
action;
|
|
|
|
config.set( {
|
|
wgTitle: 'Foo',
|
|
wgNamespaceNumber: 1,
|
|
wgArticleId: 2,
|
|
wgUserEditCount: 3
|
|
} );
|
|
|
|
stubUserSettings = {
|
|
getPreviewCount: function () {
|
|
return 22;
|
|
}
|
|
};
|
|
|
|
assert.expect( 1 );
|
|
|
|
action = mw.popups.actions.boot(
|
|
isUserInCondition,
|
|
stubUser,
|
|
stubUserSettings,
|
|
generateToken,
|
|
config
|
|
);
|
|
|
|
assert.deepEqual(
|
|
action,
|
|
{
|
|
type: 'BOOT',
|
|
sessionToken: '0123456789',
|
|
pageToken: '9876543210',
|
|
page: {
|
|
title: 'Foo',
|
|
namespaceID: 1,
|
|
id: 2
|
|
},
|
|
user: {
|
|
isInCondition: false,
|
|
isAnon: true,
|
|
editCount: 3,
|
|
previewCount: 22
|
|
}
|
|
}
|
|
);
|
|
} );
|
|
|
|
/**
|
|
* Stubs `mw.popups.wait` and adds the deferred and its promise as properties
|
|
* of the module.
|
|
*
|
|
* @param {Object} module
|
|
*/
|
|
function setupWait( module ) {
|
|
module.waitDeferred = $.Deferred();
|
|
module.waitPromise = module.waitDeferred.promise();
|
|
|
|
module.sandbox.stub( mw.popups, 'wait', function () {
|
|
return module.waitPromise;
|
|
} );
|
|
}
|
|
|
|
QUnit.module( 'ext.popups/actions#linkDwell @integration', {
|
|
setup: function () {
|
|
var that = this;
|
|
|
|
this.el = $( '<a>' )
|
|
.data( 'page-previews-title', 'Foo' )
|
|
.eq( 0 );
|
|
|
|
this.state = {};
|
|
this.getState = function () {
|
|
return that.state;
|
|
};
|
|
|
|
setupWait( this );
|
|
}
|
|
} );
|
|
|
|
QUnit.test( '#linkDwell', function ( assert ) {
|
|
var done = assert.async(),
|
|
event = {},
|
|
dispatch = this.sandbox.spy(),
|
|
gatewayDeferred = $.Deferred(),
|
|
gateway = function () {
|
|
return gatewayDeferred;
|
|
},
|
|
el = this.el,
|
|
fetchThunk,
|
|
result = {};
|
|
|
|
this.sandbox.stub( mw, 'now' ).returns( new Date() );
|
|
|
|
mw.popups.actions.linkDwell( el, event, gateway, generateToken )(
|
|
dispatch,
|
|
this.getState
|
|
);
|
|
|
|
assert.deepEqual( dispatch.getCall( 0 ).args[0], {
|
|
type: 'LINK_DWELL',
|
|
el: el,
|
|
event: event,
|
|
interactionToken: '9876543210',
|
|
timestamp: mw.now()
|
|
} );
|
|
|
|
// Stub the state tree being updated.
|
|
this.state.preview = {
|
|
enabled: true,
|
|
activeLink: el
|
|
};
|
|
|
|
// ---
|
|
|
|
this.waitPromise.then( function () {
|
|
assert.strictEqual(
|
|
dispatch.callCount,
|
|
2,
|
|
'The fetch action is dispatched after 500 ms'
|
|
);
|
|
|
|
fetchThunk = dispatch.secondCall.args[0];
|
|
fetchThunk( dispatch );
|
|
|
|
assert.ok( dispatch.calledWith( {
|
|
type: 'FETCH_START',
|
|
el: el,
|
|
title: 'Foo'
|
|
} ) );
|
|
|
|
// ---
|
|
|
|
gatewayDeferred.resolve( result );
|
|
|
|
assert.ok( dispatch.calledWith( {
|
|
type: 'FETCH_END',
|
|
el: el,
|
|
result: result
|
|
} ) );
|
|
|
|
done();
|
|
} );
|
|
|
|
// After 500 ms...
|
|
this.waitDeferred.resolve();
|
|
} );
|
|
|
|
QUnit.test( '#linkDwell doesn\'t dispatch under certain conditions', function ( assert ) {
|
|
var cases,
|
|
done,
|
|
that = this,
|
|
event = {};
|
|
|
|
cases = [
|
|
{
|
|
enabled: false
|
|
},
|
|
{
|
|
enabled: true,
|
|
activeLink: undefined // Any value other than this.el.
|
|
}
|
|
];
|
|
|
|
done = assert.async( cases.length );
|
|
|
|
$.each( cases, function ( testCase ) {
|
|
var dispatch = that.sandbox.spy();
|
|
|
|
mw.popups.actions.linkDwell( that.el, event, /* gateway = */ null, generateToken )(
|
|
dispatch,
|
|
that.getState
|
|
);
|
|
|
|
that.state.preview = testCase;
|
|
|
|
that.waitPromise.then( function () {
|
|
assert.strictEqual( dispatch.callCount, 1 );
|
|
|
|
done();
|
|
} );
|
|
|
|
// After 500 ms...
|
|
that.waitDeferred.resolve();
|
|
} );
|
|
} );
|
|
|
|
QUnit.module( 'ext.popups/actions#linkAbandon', {
|
|
setup: function () {
|
|
setupWait( this );
|
|
}
|
|
} );
|
|
|
|
QUnit.test( 'it should dispatch start and end actions', function ( assert ) {
|
|
var that = this,
|
|
dispatch = that.sandbox.spy(),
|
|
done = assert.async();
|
|
|
|
this.sandbox.stub( mw, 'now' ).returns( new Date() );
|
|
|
|
mw.popups.actions.linkAbandon( that.el )( dispatch );
|
|
|
|
assert.ok( dispatch.calledWith( {
|
|
type: 'LINK_ABANDON_START',
|
|
el: that.el,
|
|
timestamp: mw.now()
|
|
} ) );
|
|
|
|
// ---
|
|
|
|
assert.ok(
|
|
mw.popups.wait.calledWith( 300 ),
|
|
'Have you spoken with #Design about changing this value?'
|
|
);
|
|
|
|
that.waitPromise.then( function () {
|
|
assert.ok( dispatch.calledWith( {
|
|
type: 'LINK_ABANDON_END',
|
|
el: that.el
|
|
} ) );
|
|
|
|
done();
|
|
} );
|
|
|
|
// After 300 ms...
|
|
that.waitDeferred.resolve();
|
|
} );
|
|
|
|
QUnit.module( 'ext.popups/actions#previewAbandon', function ( assert ) {
|
|
var that = this,
|
|
dispatch = that.sandbox.spy(),
|
|
done = assert.async();
|
|
|
|
mw.popups.actions.previewAbandon()( dispatch );
|
|
|
|
assert.ok( dispatch.calledWith( {
|
|
type: 'PREVIEW_ABANDON_START'
|
|
} ) );
|
|
|
|
// ---
|
|
|
|
assert.ok(
|
|
mw.popups.wait.calledWith( 300 ),
|
|
'Have you spoken with #Design about changing this value?'
|
|
);
|
|
|
|
that.waitPromise.then( function () {
|
|
assert.ok( dispatch.calledWith( {
|
|
type: 'PREVIEW_ABANDON_END'
|
|
} ) );
|
|
|
|
done();
|
|
} );
|
|
|
|
// After 300 ms...
|
|
that.waitDeferred.resolve();
|
|
} );
|
|
|
|
QUnit.module( 'ext.popups/actions#saveSettings' );
|
|
|
|
QUnit.test( 'it should dispatch an action with previous and current enabled state', function ( assert ) {
|
|
var dispatch = this.sandbox.spy(),
|
|
getState = this.sandbox.stub().returns( {
|
|
preview: {
|
|
enabled: false
|
|
}
|
|
} );
|
|
|
|
mw.popups.actions.saveSettings( /* enabled = */ true )( dispatch, getState );
|
|
|
|
assert.ok( getState.calledOnce, 'it should query the global state for the current state' );
|
|
assert.ok( dispatch.calledWith( {
|
|
type: 'SETTINGS_CHANGE',
|
|
wasEnabled: false,
|
|
enabled: true
|
|
} ), 'it should dispatch the action with the previous and next enabled state' );
|
|
} );
|
|
}( mediaWiki, jQuery ) );
|