2021-04-08 13:46:09 +00:00
|
|
|
var utils = require( 'ext.discussionTools.init' ).utils;
|
2020-05-22 17:09:21 +00:00
|
|
|
|
2019-10-24 11:41:25 +00:00
|
|
|
module.exports = {};
|
|
|
|
|
2019-10-24 12:59:42 +00:00
|
|
|
/**
|
|
|
|
* Override mw.config with the given data. Used for testing different languages etc.
|
Change CommentParser into a service
Goal:
-----
To have a method like CommentParser::parse(), which just takes a node
to parse and a title and returns plain data, so that we don't need to
keep track of the config to construct a CommentParser object (the
required config like content language is provided by services) and
we don't need to keep that object around after parsing.
Changes:
--------
CommentParser.php:
* …is now a service. Constructor only takes services as arguments.
The node and title are passed to a new parse() method.
* parse() should return plain data, but I split this part to a separate
patch for ease of review: I49bfe019aa460651447fd383f73eafa9d7180a92.
* CommentParser still cheats and accesses global state in a few places,
e.g. calling Title::makeTitleSafe or CommentUtils::getTitleFromUrl,
so we can't turn its tests into true unit tests. This work is left
for future commits.
LanguageData.php:
* …is now a service, instead of a static class.
Parser.js:
* …is not a real service, but it's changed to behave in a similar way.
Constructor takes only the required config as argument,
and node and title are instead passed to a new parse() method.
CommentParserTest.php:
parser.test.js:
* Can be simplified, now that we don't need a useless node and title
to test internal methods that don't use them.
testUtils.js:
* Can be simplified, now that we don't need to override internal
ResourceLoader stuff just to change the parser config.
Change-Id: Iadb7757debe000025e52770ca51ebcf24ca8ee66
2022-02-19 02:43:21 +00:00
|
|
|
* (Automatically restored after every test by QUnit.newMwEnvironment.)
|
2019-10-24 12:59:42 +00:00
|
|
|
*
|
|
|
|
* @param {Object} config
|
|
|
|
*/
|
|
|
|
module.exports.overrideMwConfig = function ( config ) {
|
2024-05-02 15:49:43 +00:00
|
|
|
Object.assign(
|
2019-10-24 12:59:42 +00:00
|
|
|
mw.config.values,
|
|
|
|
config
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-03-09 19:32:51 +00:00
|
|
|
/**
|
|
|
|
* Return the node that is expected to contain thread items.
|
|
|
|
*
|
2022-10-12 20:03:23 +00:00
|
|
|
* @param {Document} doc
|
|
|
|
* @return {Element}
|
2022-03-09 19:32:51 +00:00
|
|
|
*/
|
2022-10-12 20:03:23 +00:00
|
|
|
module.exports.getThreadContainer = function ( doc ) {
|
|
|
|
// In tests created from Parsoid output, comments are contained directly in <body>.
|
2022-03-09 19:32:51 +00:00
|
|
|
// In tests created from old parser output, comments are contained in <div class="mw-parser-output">.
|
2022-10-12 20:03:23 +00:00
|
|
|
var body = doc.body;
|
|
|
|
var wrapper = body.querySelector( 'div.mw-parser-output' );
|
|
|
|
return wrapper || body;
|
2022-03-09 19:32:51 +00:00
|
|
|
};
|
|
|
|
|
2019-10-24 12:59:42 +00:00
|
|
|
/**
|
|
|
|
* Get the offset path from ancestor to offset in descendant
|
|
|
|
*
|
|
|
|
* @param {Node} ancestor The ancestor node
|
|
|
|
* @param {Node} node The descendant node
|
|
|
|
* @param {number} nodeOffset The offset in the descendant node
|
2022-10-04 12:50:57 +00:00
|
|
|
* @return {string} The offset path
|
2019-10-24 12:59:42 +00:00
|
|
|
*/
|
|
|
|
function getOffsetPath( ancestor, node, nodeOffset ) {
|
|
|
|
var path = [ nodeOffset ];
|
|
|
|
while ( node !== ancestor ) {
|
|
|
|
if ( node.parentNode === null ) {
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.log( node, 'is not a descendant of', ancestor );
|
|
|
|
throw new Error( 'Not a descendant' );
|
|
|
|
}
|
2020-05-22 17:09:21 +00:00
|
|
|
path.unshift( utils.childIndexOf( node ) );
|
2019-10-24 12:59:42 +00:00
|
|
|
node = node.parentNode;
|
|
|
|
}
|
2022-10-04 12:50:57 +00:00
|
|
|
return path.join( '/' );
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPathsFromRange( root, range ) {
|
|
|
|
return [
|
|
|
|
getOffsetPath( root, range.startContainer, range.startOffset ),
|
|
|
|
getOffsetPath( root, range.endContainer, range.endOffset )
|
|
|
|
];
|
2019-10-24 12:59:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Massage comment data to make it serializable as JSON.
|
|
|
|
*
|
2020-07-19 19:47:44 +00:00
|
|
|
* @param {CommentItem} parent Comment item; modified in-place
|
2019-10-24 12:59:42 +00:00
|
|
|
* @param {Node} root Ancestor node of all comments
|
|
|
|
*/
|
|
|
|
module.exports.serializeComments = function ( parent, root ) {
|
2020-10-01 19:36:11 +00:00
|
|
|
if ( !parent.range.startContainer ) {
|
|
|
|
// Already done as part of a different thread
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-24 12:59:42 +00:00
|
|
|
// Can't serialize circular structures to JSON
|
|
|
|
delete parent.parent;
|
|
|
|
|
|
|
|
// Can't serialize the DOM nodes involved in the range,
|
|
|
|
// instead use their offsets within their parent nodes
|
2022-10-04 12:50:57 +00:00
|
|
|
parent.range = getPathsFromRange( root, parent.range );
|
2020-03-04 16:14:47 +00:00
|
|
|
if ( parent.signatureRanges ) {
|
2024-04-19 22:07:35 +00:00
|
|
|
parent.signatureRanges = parent.signatureRanges.map( ( range ) => getPathsFromRange( root, range ) );
|
2022-10-04 12:50:57 +00:00
|
|
|
}
|
|
|
|
if ( parent.timestampRanges ) {
|
2024-04-19 22:07:35 +00:00
|
|
|
parent.timestampRanges = parent.timestampRanges.map( ( range ) => getPathsFromRange( root, range ) );
|
2020-03-04 16:14:47 +00:00
|
|
|
}
|
2022-10-11 16:37:20 +00:00
|
|
|
if ( parent.timestamp ) {
|
|
|
|
parent.timestamp = parent.getTimestampString();
|
|
|
|
}
|
2022-02-04 18:16:24 +00:00
|
|
|
if ( !parent.displayName ) {
|
|
|
|
delete parent.displayName;
|
|
|
|
}
|
2019-10-24 12:59:42 +00:00
|
|
|
|
2020-07-29 23:57:51 +00:00
|
|
|
// Unimportant
|
|
|
|
delete parent.rootNode;
|
|
|
|
|
2022-07-05 23:21:34 +00:00
|
|
|
// Ignore generated properties
|
|
|
|
delete parent.authors;
|
|
|
|
delete parent.commentCount;
|
|
|
|
delete parent.oldestReply;
|
|
|
|
delete parent.latestReply;
|
|
|
|
|
2024-04-18 18:37:58 +00:00
|
|
|
parent.replies.forEach( ( comment ) => {
|
2019-10-24 12:59:42 +00:00
|
|
|
module.exports.serializeComments( comment, root );
|
|
|
|
} );
|
|
|
|
};
|