2017-07-28 17:32:46 +00:00
|
|
|
import preview from '../../../src/reducers/preview';
|
2018-01-11 15:44:26 +00:00
|
|
|
import actionTypes from '../../../src/actionTypes';
|
2018-01-11 18:56:56 +00:00
|
|
|
import { createNullModel } from '../../../src/preview/model';
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
QUnit.module( 'ext.popups/reducers#preview', {
|
2018-03-14 22:04:59 +00:00
|
|
|
beforeEach() {
|
2017-02-15 15:00:16 +00:00
|
|
|
this.el = 'active link';
|
|
|
|
}
|
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( '@@INIT', ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const state = preview( undefined, { type: '@@INIT' } );
|
2017-02-15 15:00:16 +00:00
|
|
|
|
2018-05-08 19:48:17 +00:00
|
|
|
assert.expect( 1, 'All assertions are executed.' );
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
state,
|
|
|
|
{
|
|
|
|
enabled: undefined,
|
|
|
|
activeLink: undefined,
|
|
|
|
activeEvent: undefined,
|
|
|
|
activeToken: '',
|
|
|
|
shouldShow: false,
|
2019-04-05 10:31:54 +00:00
|
|
|
isUserDwelling: false,
|
|
|
|
wasClicked: false
|
2018-05-08 19:48:17 +00:00
|
|
|
},
|
|
|
|
'The initial state is correct.'
|
2017-02-15 15:00:16 +00:00
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( 'BOOT', ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.BOOT,
|
2017-02-15 15:00:16 +00:00
|
|
|
isEnabled: true
|
|
|
|
};
|
|
|
|
|
2018-05-08 19:48:17 +00:00
|
|
|
assert.expect( 1, 'All assertions are executed.' );
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( {}, action ),
|
|
|
|
{
|
|
|
|
enabled: true
|
|
|
|
},
|
|
|
|
'It should set whether or not previews are enabled.'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( 'SETTINGS_CHANGE', ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.SETTINGS_CHANGE,
|
2017-02-15 15:00:16 +00:00
|
|
|
enabled: true
|
|
|
|
};
|
|
|
|
|
2018-05-08 19:48:17 +00:00
|
|
|
assert.expect( 1, 'All assertions are executed.' );
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( {}, action ),
|
|
|
|
{
|
|
|
|
enabled: true
|
|
|
|
},
|
|
|
|
'It should set whether or not previews are enabled when settings change.'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
|
|
|
QUnit.test( 'LINK_DWELL initializes the state for a new link', function ( assert ) {
|
Update: cancel unused HTTP requests in flight
Whenever an HTTP request sequence is started, i.e. wait for the fetch
start time, issue a network request, and return the result, abort the
process if the results are known to no longer be needed. This occurs
when a user has dwelt upon one link and then abandoned it either during
the fetch start wait time or during the fetch network request itself.
This change is accomplished by preserving the pending promises in two
actions, LINK_DWELL and FETCH_START, and whenever the ABANDON_START
action is issued, it now aborts any previously pending XHR-like promise,
called a "AbortPromise" which is just a thenable with an abort() method.
There is a similar concept in Core:
https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/ecc812f06e7dff587b3f31dc18189adbf4616351/resources/src/mediawiki.api/index.js.
Aborting pending requests has big implications for client and server
logging as requests are quickly canceled, especially on slower
connections. These differences can be observed on the network tab of
DevTools and the log in Redux DevTools.
Consider, for instance, the scenario of dwelling upon and quickly
abandoning a single link prior to this patch:
BOOT EVENT_LOGGED LINK_DWELL FETCH_START ABANDON_START FETCH_END STATSV_LOGGED ABANDON_END EVENT_LOGGED FETCH_COMPLETE
And after this patch when the fetch timer is canceled (prior to an
actual network request):
BOOT EVENT_LOGGED LINK_DWELL ABANDON_START ABANDON_END EVENT_LOGGED
In the above sequence, FETCH_* and STATSV_LOGGED actions never occur.
And after this patch when the network request itself is canceled:
BOOT EVENT_LOGGED LINK_DWELL FETCH_START ABANDON_START FETCH_FAILED STATSV_LOGGED FETCH_COMPLETE ABANDON_END EVENT_LOGGED
FETCH_FAILED occurs intentionally, STATSV_LOGGED and FETCH_COMPLETE
still happen even though the fetch didn't complete successfully, and
FETCH_END doesn't.
Additionally, since less data is transmitted, it's possible that the
timing and success rate of logging will improve on low bandwidth
connections.
Also, this patch tries to revise the JSDocs where possible to support
type checking and fix a call to the missing assert.fail() function in
changeListener.test.js.
Bug: T197700
Change-Id: I9a73b3086fc8fb0edd897a347b5497d5362e20ef
2018-06-25 13:26:11 +00:00
|
|
|
const promise = $.Deferred().promise();
|
2018-03-19 19:39:41 +00:00
|
|
|
const action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.LINK_DWELL,
|
2017-02-15 15:00:16 +00:00
|
|
|
el: this.el,
|
|
|
|
event: {},
|
Update: cancel unused HTTP requests in flight
Whenever an HTTP request sequence is started, i.e. wait for the fetch
start time, issue a network request, and return the result, abort the
process if the results are known to no longer be needed. This occurs
when a user has dwelt upon one link and then abandoned it either during
the fetch start wait time or during the fetch network request itself.
This change is accomplished by preserving the pending promises in two
actions, LINK_DWELL and FETCH_START, and whenever the ABANDON_START
action is issued, it now aborts any previously pending XHR-like promise,
called a "AbortPromise" which is just a thenable with an abort() method.
There is a similar concept in Core:
https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/ecc812f06e7dff587b3f31dc18189adbf4616351/resources/src/mediawiki.api/index.js.
Aborting pending requests has big implications for client and server
logging as requests are quickly canceled, especially on slower
connections. These differences can be observed on the network tab of
DevTools and the log in Redux DevTools.
Consider, for instance, the scenario of dwelling upon and quickly
abandoning a single link prior to this patch:
BOOT EVENT_LOGGED LINK_DWELL FETCH_START ABANDON_START FETCH_END STATSV_LOGGED ABANDON_END EVENT_LOGGED FETCH_COMPLETE
And after this patch when the fetch timer is canceled (prior to an
actual network request):
BOOT EVENT_LOGGED LINK_DWELL ABANDON_START ABANDON_END EVENT_LOGGED
In the above sequence, FETCH_* and STATSV_LOGGED actions never occur.
And after this patch when the network request itself is canceled:
BOOT EVENT_LOGGED LINK_DWELL FETCH_START ABANDON_START FETCH_FAILED STATSV_LOGGED FETCH_COMPLETE ABANDON_END EVENT_LOGGED
FETCH_FAILED occurs intentionally, STATSV_LOGGED and FETCH_COMPLETE
still happen even though the fetch didn't complete successfully, and
FETCH_END doesn't.
Additionally, since less data is transmitted, it's possible that the
timing and success rate of logging will improve on low bandwidth
connections.
Also, this patch tries to revise the JSDocs where possible to support
type checking and fix a call to the missing assert.fail() function in
changeListener.test.js.
Bug: T197700
Change-Id: I9a73b3086fc8fb0edd897a347b5497d5362e20ef
2018-06-25 13:26:11 +00:00
|
|
|
token: '1234567890',
|
|
|
|
promise
|
2017-02-15 15:00:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( {}, action ),
|
|
|
|
{
|
|
|
|
activeLink: action.el,
|
|
|
|
activeEvent: action.event,
|
|
|
|
activeToken: action.token,
|
|
|
|
shouldShow: false,
|
Update: cancel unused HTTP requests in flight
Whenever an HTTP request sequence is started, i.e. wait for the fetch
start time, issue a network request, and return the result, abort the
process if the results are known to no longer be needed. This occurs
when a user has dwelt upon one link and then abandoned it either during
the fetch start wait time or during the fetch network request itself.
This change is accomplished by preserving the pending promises in two
actions, LINK_DWELL and FETCH_START, and whenever the ABANDON_START
action is issued, it now aborts any previously pending XHR-like promise,
called a "AbortPromise" which is just a thenable with an abort() method.
There is a similar concept in Core:
https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/core/+/ecc812f06e7dff587b3f31dc18189adbf4616351/resources/src/mediawiki.api/index.js.
Aborting pending requests has big implications for client and server
logging as requests are quickly canceled, especially on slower
connections. These differences can be observed on the network tab of
DevTools and the log in Redux DevTools.
Consider, for instance, the scenario of dwelling upon and quickly
abandoning a single link prior to this patch:
BOOT EVENT_LOGGED LINK_DWELL FETCH_START ABANDON_START FETCH_END STATSV_LOGGED ABANDON_END EVENT_LOGGED FETCH_COMPLETE
And after this patch when the fetch timer is canceled (prior to an
actual network request):
BOOT EVENT_LOGGED LINK_DWELL ABANDON_START ABANDON_END EVENT_LOGGED
In the above sequence, FETCH_* and STATSV_LOGGED actions never occur.
And after this patch when the network request itself is canceled:
BOOT EVENT_LOGGED LINK_DWELL FETCH_START ABANDON_START FETCH_FAILED STATSV_LOGGED FETCH_COMPLETE ABANDON_END EVENT_LOGGED
FETCH_FAILED occurs intentionally, STATSV_LOGGED and FETCH_COMPLETE
still happen even though the fetch didn't complete successfully, and
FETCH_END doesn't.
Additionally, since less data is transmitted, it's possible that the
timing and success rate of logging will improve on low bandwidth
connections.
Also, this patch tries to revise the JSDocs where possible to support
type checking and fix a call to the missing assert.fail() function in
changeListener.test.js.
Bug: T197700
Change-Id: I9a73b3086fc8fb0edd897a347b5497d5362e20ef
2018-06-25 13:26:11 +00:00
|
|
|
isUserDwelling: true,
|
|
|
|
promise
|
2017-02-15 15:00:16 +00:00
|
|
|
},
|
|
|
|
'It should set active link and event as well as interaction info and hide the preview.'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
|
|
|
QUnit.test( 'LINK_DWELL on an active link only updates dwell state', function ( assert ) {
|
2018-03-19 19:39:41 +00:00
|
|
|
const action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.LINK_DWELL,
|
2017-02-15 15:00:16 +00:00
|
|
|
el: this.el,
|
|
|
|
event: {},
|
|
|
|
token: '1234567890'
|
|
|
|
},
|
|
|
|
state = {
|
|
|
|
activeLink: this.el,
|
|
|
|
isUserDwelling: false
|
|
|
|
};
|
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( state, action ),
|
|
|
|
{
|
|
|
|
activeLink: this.el,
|
|
|
|
isUserDwelling: true
|
|
|
|
},
|
|
|
|
'It should only set isUserDwelling to true'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( 'ABANDON_END', ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.ABANDON_END,
|
2018-03-19 19:39:41 +00:00
|
|
|
token: 'bananas'
|
|
|
|
};
|
|
|
|
let state = {
|
|
|
|
activeToken: 'bananas',
|
|
|
|
isUserDwelling: false
|
|
|
|
};
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( state, action ),
|
|
|
|
{
|
|
|
|
activeLink: undefined,
|
|
|
|
activeToken: undefined,
|
|
|
|
activeEvent: undefined,
|
|
|
|
fetchResponse: undefined,
|
|
|
|
isUserDwelling: false,
|
|
|
|
shouldShow: false
|
|
|
|
},
|
|
|
|
'ABANDON_END should hide the preview and reset the interaction info.'
|
|
|
|
);
|
|
|
|
|
|
|
|
// ---
|
|
|
|
|
|
|
|
state = {
|
|
|
|
activeToken: 'apples',
|
|
|
|
isUserDwelling: true
|
|
|
|
};
|
|
|
|
|
2018-05-20 12:32:51 +00:00
|
|
|
assert.strictEqual(
|
2017-02-15 15:00:16 +00:00
|
|
|
preview( state, action ),
|
|
|
|
state,
|
|
|
|
'ABANDON_END should NOOP if the current interaction has changed.'
|
|
|
|
);
|
|
|
|
|
|
|
|
// ---
|
|
|
|
|
|
|
|
state = {
|
|
|
|
activeToken: 'bananas',
|
|
|
|
isUserDwelling: true
|
|
|
|
};
|
|
|
|
|
2018-05-20 12:32:51 +00:00
|
|
|
assert.strictEqual(
|
2017-02-15 15:00:16 +00:00
|
|
|
preview( state, action ),
|
|
|
|
state,
|
|
|
|
'ABANDON_END should NOOP if the user is dwelling on the preview.'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( 'FETCH_COMPLETE', ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const token = '1234567890';
|
|
|
|
let state = {
|
2017-04-21 10:59:38 +00:00
|
|
|
activeToken: token,
|
|
|
|
isUserDwelling: true
|
2017-02-15 15:00:16 +00:00
|
|
|
},
|
|
|
|
action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.FETCH_COMPLETE,
|
2018-03-14 19:44:22 +00:00
|
|
|
token,
|
2017-02-15 15:00:16 +00:00
|
|
|
result: {}
|
|
|
|
};
|
|
|
|
|
2018-05-08 19:48:17 +00:00
|
|
|
assert.expect( 3, 'All assertions are executed.' );
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( state, action ),
|
|
|
|
{
|
2017-04-21 10:59:38 +00:00
|
|
|
// Previous state.
|
|
|
|
activeToken: state.activeToken,
|
|
|
|
isUserDwelling: true,
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
fetchResponse: action.result,
|
|
|
|
shouldShow: true
|
|
|
|
},
|
|
|
|
'It should store the result and signal that a preview should be rendered.'
|
|
|
|
);
|
|
|
|
|
|
|
|
// ---
|
|
|
|
|
2017-04-21 10:59:38 +00:00
|
|
|
state = preview( state, {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.ABANDON_START,
|
2018-03-14 19:44:22 +00:00
|
|
|
token
|
2017-04-21 10:59:38 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( state, action ),
|
|
|
|
{
|
|
|
|
activeToken: token,
|
|
|
|
isUserDwelling: false, // Set when ABANDON_START is reduced.
|
2019-04-05 10:31:54 +00:00
|
|
|
wasClicked: false,
|
2017-04-21 10:59:38 +00:00
|
|
|
|
|
|
|
fetchResponse: action.result,
|
|
|
|
shouldShow: false
|
|
|
|
},
|
|
|
|
'It shouldn\'t signal that a preview should be rendered if the user has abandoned the link since the gateway request was made.'
|
|
|
|
);
|
|
|
|
|
|
|
|
// ---
|
|
|
|
|
2017-02-15 15:00:16 +00:00
|
|
|
action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.FETCH_COMPLETE,
|
2017-04-07 12:40:55 +00:00
|
|
|
token: 'banana',
|
2017-02-15 15:00:16 +00:00
|
|
|
result: {}
|
|
|
|
};
|
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( state, action ),
|
|
|
|
state,
|
2017-04-21 10:59:38 +00:00
|
|
|
'It should NOOP if the user has interacted with another link since the gateway request was made.'
|
2017-02-15 15:00:16 +00:00
|
|
|
);
|
2017-04-21 10:59:38 +00:00
|
|
|
|
2017-02-15 15:00:16 +00:00
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( actionTypes.FETCH_FAILED, ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const token = '1234567890',
|
2018-01-11 15:44:26 +00:00
|
|
|
state = {
|
|
|
|
activeToken: token,
|
|
|
|
isUserDwelling: true
|
|
|
|
};
|
2018-03-19 19:39:41 +00:00
|
|
|
let action = {
|
|
|
|
type: actionTypes.FETCH_FAILED,
|
|
|
|
token
|
|
|
|
};
|
2018-01-11 15:44:26 +00:00
|
|
|
|
2018-05-08 19:48:17 +00:00
|
|
|
assert.expect( 2, 'All assertions are executed.' );
|
2018-01-11 15:44:26 +00:00
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( state, action ),
|
|
|
|
{
|
|
|
|
activeToken: state.activeToken,
|
|
|
|
isUserDwelling: true
|
|
|
|
},
|
2018-01-11 18:56:56 +00:00
|
|
|
'It should not transition states.'
|
|
|
|
);
|
|
|
|
|
|
|
|
// ---
|
|
|
|
|
|
|
|
action = {
|
|
|
|
type: actionTypes.FETCH_COMPLETE,
|
2018-03-14 19:44:22 +00:00
|
|
|
token,
|
2018-01-11 18:56:56 +00:00
|
|
|
result: { title: createNullModel( 'Title', '/wiki/Title' ) }
|
|
|
|
};
|
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( state, action ),
|
|
|
|
{
|
|
|
|
activeToken: state.activeToken,
|
|
|
|
isUserDwelling: true,
|
|
|
|
|
|
|
|
fetchResponse: action.result,
|
|
|
|
shouldShow: true
|
|
|
|
},
|
|
|
|
'It should store the result and signal that an error preview should be rendered.'
|
2018-01-11 15:44:26 +00:00
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( 'PREVIEW_DWELL', ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.PREVIEW_DWELL
|
2017-02-15 15:00:16 +00:00
|
|
|
};
|
|
|
|
|
2018-05-08 19:48:17 +00:00
|
|
|
assert.expect( 1, 'All assertions are executed.' );
|
2017-02-15 15:00:16 +00:00
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( {}, action ),
|
|
|
|
{
|
|
|
|
isUserDwelling: true
|
|
|
|
},
|
|
|
|
'It should mark the preview as being dwelled on.'
|
|
|
|
);
|
|
|
|
} );
|
|
|
|
|
2018-03-14 23:50:09 +00:00
|
|
|
QUnit.test( 'ABANDON_START', ( assert ) => {
|
2018-03-19 19:39:41 +00:00
|
|
|
const action = {
|
2018-07-13 15:12:49 +00:00
|
|
|
type: actionTypes.ABANDON_START
|
2017-02-15 15:00:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert.deepEqual(
|
|
|
|
preview( {}, action ),
|
|
|
|
{
|
2019-04-05 10:31:54 +00:00
|
|
|
isUserDwelling: false,
|
|
|
|
wasClicked: false
|
2017-02-15 15:00:16 +00:00
|
|
|
},
|
|
|
|
'ABANDON_START should mark the preview having been abandoned.'
|
|
|
|
);
|
|
|
|
} );
|