mediawiki-extensions-Discus.../tests/qunit/testUtils.js

105 lines
2.9 KiB
JavaScript
Raw Normal View History

var utils = require( 'ext.discussionTools.init' ).utils;
module.exports = {};
/**
* 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.)
*
* @param {Object} config
*/
module.exports.overrideMwConfig = function ( config ) {
Object.assign(
mw.config.values,
config
);
};
/**
* Return the node that is expected to contain thread items.
*
* @param {Document} doc
* @return {Element}
*/
module.exports.getThreadContainer = function ( doc ) {
// In tests created from Parsoid output, comments are contained directly in <body>.
// In tests created from old parser output, comments are contained in <div class="mw-parser-output">.
var body = doc.body;
var wrapper = body.querySelector( 'div.mw-parser-output' );
return wrapper || body;
};
/**
* 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
* @return {string} The offset path
*/
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' );
}
path.unshift( utils.childIndexOf( node ) );
node = node.parentNode;
}
return path.join( '/' );
}
function getPathsFromRange( root, range ) {
return [
getOffsetPath( root, range.startContainer, range.startOffset ),
getOffsetPath( root, range.endContainer, range.endOffset )
];
}
/**
* Massage comment data to make it serializable as JSON.
*
* @param {CommentItem} parent Comment item; modified in-place
* @param {Node} root Ancestor node of all comments
*/
module.exports.serializeComments = function ( parent, root ) {
if ( !parent.range.startContainer ) {
// Already done as part of a different thread
return;
}
// 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
parent.range = getPathsFromRange( root, parent.range );
if ( parent.signatureRanges ) {
parent.signatureRanges = parent.signatureRanges.map( ( range ) => getPathsFromRange( root, range ) );
}
if ( parent.timestampRanges ) {
parent.timestampRanges = parent.timestampRanges.map( ( range ) => getPathsFromRange( root, range ) );
}
if ( parent.timestamp ) {
parent.timestamp = parent.getTimestampString();
}
if ( !parent.displayName ) {
delete parent.displayName;
}
// Unimportant
delete parent.rootNode;
// Ignore generated properties
delete parent.authors;
delete parent.commentCount;
delete parent.oldestReply;
delete parent.latestReply;
parent.replies.forEach( ( comment ) => {
module.exports.serializeComments( comment, root );
} );
};