mudlet/test/TMxpCustomElementTagHandlerTest.cpp
eowmob beb6d70324
Fix: Tooltip hints for MXP hints no longer forced to uppercase (#6878)
<!-- Keep the title short & concise so anyone non-technical can
understand it,
     the title appears in PTB changelogs -->
#### Brief overview of PR changes/additions
In some, but not all, occasions the tooltip hints of MXP hints were
forced to all upper case by Mudlet
#### Motivation for adding to Mudlet
Improvement of MXP compatibility, more uniform menu appearance.
#### Other info (issues closed, discussion etc)
Mudlet forced HINT attributes used in custom elements to upper case, but
only there. Note that entities were interpolated afterwards, hence not
forced to uppercase. I think the behaviour is inconsistent and not
'logically' comprehensible for a user.
Afaik, if the mud specifies a HINT text it should be display as such. If
the game wants it to show up in UPPERCASE the game should just say so.
**However,** in difference to my other MXP patches, it will change the
look of the GUI for games that somehow depend on it.
So.. it can be argued.

It does change the non uniform display of hints like those two from the
unfixed version:

![send_b4](https://github.com/Mudlet/Mudlet/assets/72653851/71507e6e-6ae7-437b-87bc-d0ddbaf00eea)
and

![sendprompt_b4](https://github.com/Mudlet/Mudlet/assets/72653851/1dfd75be-9ba8-4dac-83d6-0883cb3c8bf5)
to 

![send_after](https://github.com/Mudlet/Mudlet/assets/72653851/2fc2e4f3-c21c-411a-bc92-d6325077fccb)

![sendprompt_after](https://github.com/Mudlet/Mudlet/assets/72653851/fb2e5c0b-8574-4a72-8366-f733a6bd8bd0)

Also, it used to deal with HINTs only prior to entity resolution, so
without the fix it looked like

![mixedmenu_b4](https://github.com/Mudlet/Mudlet/assets/72653851/3f824de0-7245-4ead-8b4f-f25c561d603e)
rather than 

![mixedmenu_after](https://github.com/Mudlet/Mudlet/assets/72653851/97e23ee2-6c50-4c49-9be7-561402ef1573)

And the only difference between these two menus (both from the unfixed
version)

![send_plain](https://github.com/Mudlet/Mudlet/assets/72653851/5acf8ea3-6134-4b4d-93b1-6ac6ded77944)

![send_custom_element](https://github.com/Mudlet/Mudlet/assets/72653851/cfb47c73-0118-4f7d-acf8-cef2f43bc896)
is that the first uses a plain `<send>` while the second uses a custom
element.

Again, you can see this in action by connecting to aldebaran-mud.de 2000
, login as guest (y to confirm) and with 'set mxp test 4'.
2023-06-11 16:45:58 +02:00

287 lines
12 KiB
C++

/***************************************************************************
* Copyright (C) 2020 by Gustavo Sousa - gustavocms@gmail.com *
* Copyright (C) 2020 by Stephen Lyons - slysven@virginmedia.com *
* Copyright (C) 2023 by Michael Weller - michael.weller@t-online.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <QTest>
#include <TMxpSendTagHandler.h>
#include <TMxpElementDefinitionHandler.h>
#include <TMxpCustomElementTagHandler.h>
#include <TMxpFormattingTagsHandler.h>
#include <TMxpColorTagHandler.h>
#include <TMxpTagParser.h>
#include <TMxpTagProcessor.h>
#include "TMxpStubClient.h"
/*
* One can certainly argue if this should be in a separate module, but
* I want to include only the handlers needed by this file into the module
* for a sensible link time and executable size
*/
class TMxpStubHandlerContext : public TMxpStubContext {
TMxpSendTagHandler sendTagHandler;
TMxpFormattingTagsHandler formattingTagsHandler;
TMxpColorTagHandler colorTagHandler;
public:
virtual TMxpTagHandlerResult handleTag(TMxpContext& ctx, TMxpClient& client, MxpTag* tag)
{
TMxpTagHandler *tagHandler;
if (sendTagHandler.supports(ctx, client, tag)) {
tagHandler = &sendTagHandler;
} else if (formattingTagsHandler.supports(ctx, client, tag)) {
tagHandler = &formattingTagsHandler;
} else if (colorTagHandler.supports(ctx, client, tag)) {
tagHandler = &colorTagHandler;
} else {
qDebug() << QString("unhandled Tag: [%1%2]").arg(tag->isEndTag() ? "/" : "").arg(tag->getName());
return MXP_TAG_HANDLED;
}
qDebug() << QString("handleTag([%1%2])").arg(tag->isEndTag() ? "/" : "").arg(tag->getName());
return tag->isStartTag() ? tagHandler->handleStartTag(ctx, client, tag->asStartTag()) : tagHandler->handleEndTag(ctx, client, tag->asEndTag());
}
};
class TMxpCustomElementTagHandlerTest : public QObject {
Q_OBJECT
private:
private slots:
QSharedPointer<MxpNode> parseNode(const QString& tagText) const
{
auto nodes = TMxpTagParser::parseToMxpNodeList(tagText);
return nodes.size() > 0 ? nodes.first() : nullptr;
}
void testCustomItemCmd() {
// Complex Example: Aldebaran defines a profile with definitions like this:
// <!EL ITI '<SEND "examine &ID;|drop &ID;" HINT="&CMDH;examine|drop">' ATT='ID'>
// Then uses <ITI inv##N>a rock</ITI> for brevity all over the mud (here plain ITem in Inventory)
// Note the use of positional parameter ID (first arg)
// Mudlet, up to version 4.10, made the hints ALL UPPER in this context
TMxpStubHandlerContext ctx;
TMxpStubClient stub;
TMxpTagParser parser;
ctx.getEntityResolver().registerEntity("&CMDH;", "");
auto defTag = parseNode("<!EL ITI '<SEND \"examine &ID;|drop &ID;\" HINT=\"&CMDH;examine|drop\">' ATT='ID'>)");
auto startTag = parseNode("<ITI inv##10>");
auto endTag = parseNode("</ITI>");
TMxpElementDefinitionHandler definitionHandler;
definitionHandler.handleTag(ctx, stub, defTag->asStartTag());
TMxpCustomElementTagHandler customElementTagHandler;
TMxpTagHandler& tagHandler = customElementTagHandler;
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("a rock");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
QCOMPARE(stub.mHrefs.size(), 2);
QCOMPARE(stub.mHrefs[0], "send([[examine inv##10]])");
QCOMPARE(stub.mHrefs[1], "send([[drop inv##10]])");
QCOMPARE(stub.mHints.size(), 2);
QCOMPARE(stub.mHints[0], "examine");
QCOMPARE(stub.mHints[1], "drop");
}
void testCustomElementDynamicEntity() {
// Complex Example: Aldebaran has a global custom element MAP used in maps printed for
// the player to click onto it and then follow the map.
// However, it uses the id of the map currently looked at in an entity redefined for
// each map you look at.
// <!EL ITI '<SEND "examine &ID;|drop &ID;" HINT="&CMDH;examine|drop">' ATT='ID'>
// Then uses <ITI inv##N>a rock</ITI> for brevity all over the mud (here plain ITem in Inventory)
// Note the use of positional parameter ID (first arg)
// Mudlet, up to version 4.10, made the hints ALL UPPER in this context
TMxpStubHandlerContext ctx;
TMxpStubClient stub;
TMxpTagParser parser;
ctx.getEntityResolver().registerEntity("&MID;", "map of the newbie jungle");
auto defTag = parseNode("<!EL MAP '<SEND \"follow &MID; to &ID;\" HINT=\"go here\">' ATT='ID'>");
TMxpElementDefinitionHandler definitionHandler;
definitionHandler.handleTag(ctx, stub, defTag->asStartTag());
auto startTag = parseNode("<MAP P8x7>");
auto endTag = parseNode("</MAP>");
TMxpCustomElementTagHandler customElementTagHandler;
TMxpTagHandler& tagHandler = customElementTagHandler;
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("*");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
QCOMPARE(stub.mHrefs.size(), 1);
QCOMPARE(stub.mHrefs[0], "send([[follow map of the newbie jungle to P8x7]])");
QCOMPARE(stub.mHints.size(), 1);
QCOMPARE(stub.mHints[0], "go here");
// Now player looks at another map and gets an MXP button to go to another place.
// Note the MAP element is NOT redefined, only the entity.
ctx.getEntityResolver().registerEntity("&MID;", "map of the south forest");
startTag = parseNode("<MAP P42>");
endTag = parseNode("</MAP>");
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("*");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
QCOMPARE(stub.mHrefs.size(), 1);
QCOMPARE(stub.mHrefs[0], "send([[follow map of the south forest to P42]])");
QCOMPARE(stub.mHints.size(), 1);
QCOMPARE(stub.mHints[0], "go here");
}
void testCustomElementDefaultAttributes() {
// This example literally taken from the MXP definition at https://www.zuggsoft.com/zmud/mxp.htm#ELEMENT
//
// <!ELEMENT boldtext '<COLOR &col;><B>' ATT='col=red'>
//
// Then you could use it on the MUD like this:
//
// <boldtext>This is bold red</boldtext>
// <boldtext col=blue>This is bold blue text</boldtext>
// <boldtext blue>This is also bold blue text</boldtext>
TMxpStubHandlerContext ctx;
TMxpStubClient stub;
TMxpTagParser parser;
auto defTag = parseNode("<!ELEMENT boldtext '<COLOR &col;><B>' ATT='col=red'>");
TMxpElementDefinitionHandler definitionHandler;
definitionHandler.handleTag(ctx, stub, defTag->asStartTag());
auto startTag = parseNode("<boldtext>");
auto endTag = parseNode("</boldtext>");
TMxpCustomElementTagHandler customElementTagHandler;
TMxpTagHandler& tagHandler = customElementTagHandler;
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("This is bold red");
// is it?
QCOMPARE(stub.bold(), true);
QCOMPARE(stub.fgColor, "red");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
// back to defaults:
QCOMPARE(stub.bold(), false);
QCOMPARE(stub.fgColor, "");
startTag = parseNode("<boldtext COL=blue>)");
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("This is bold blue text");
// is it?
QCOMPARE(stub.bold(), true);
QCOMPARE(stub.fgColor, "blue");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
// back to defaults:
QCOMPARE(stub.bold(), false);
QCOMPARE(stub.fgColor, "");
startTag = parseNode("<boldtext blue>)");
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("This is also bold blue text");
// is it?
QCOMPARE(stub.bold(), true);
QCOMPARE(stub.fgColor, "blue");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
// back to defaults:
QCOMPARE(stub.bold(), false);
QCOMPARE(stub.fgColor, "");
}
void testCustomElementPlayer() {
// Real life example from Aldebaran: (there are more sensible ones with EXPIRE which Mudlet does not yet support)
//
// <!EL WH '<SEND "whisper &NAME; |finger &NAME; |tell &NAME; " HINT="whisper &NAME;|finger &NAME;|tell &NAME;" PROMPT>' ATT='NAME=someone'>
//
// Used like <WH playerid>Player</WH> says: Hello!
// However, if player is invisible, playerid is empty, like <WH >Someone</WH> says: Hello!
//
// Upper and lower case are mixed in the entity name to make this a more severe test case
TMxpStubHandlerContext ctx;
TMxpStubClient stub;
TMxpTagParser parser;
auto defTag = parseNode("<!EL WH '<SEND \"whisper &Name; |finger &NAme; |tell &namE; \" HINT=\"whisper &name;|finger &NAME;|tell &NAME;\" PROMPT>' ATT='NAme=someone'>");
TMxpElementDefinitionHandler definitionHandler;
definitionHandler.handleTag(ctx, stub, defTag->asStartTag());
auto startTag = parseNode("<WH playerid>");
auto endTag = parseNode("</WH>");
TMxpCustomElementTagHandler customElementTagHandler;
TMxpTagHandler& tagHandler = customElementTagHandler;
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("Player");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
QCOMPARE(stub.mHrefs.size(), 3);
QCOMPARE(stub.mHrefs[0], "printCmdLine([[whisper playerid ]])");
QCOMPARE(stub.mHrefs[1], "printCmdLine([[finger playerid ]])");
QCOMPARE(stub.mHrefs[2], "printCmdLine([[tell playerid ]])");
QCOMPARE(stub.mHints.size(), 3);
QCOMPARE(stub.mHints[0], "whisper playerid");
QCOMPARE(stub.mHints[1], "finger playerid");
QCOMPARE(stub.mHints[2], "tell playerid");
// Now w/o a NAME parameter given:
startTag = parseNode("<WH>");
tagHandler.handleTag(ctx, stub, startTag->asStartTag());
tagHandler.handleContent("Invisible SuperAdmin");
tagHandler.handleTag(ctx, stub, endTag->asEndTag());
QCOMPARE(stub.mHrefs.size(), 3);
QCOMPARE(stub.mHrefs[0], "printCmdLine([[whisper someone ]])");
QCOMPARE(stub.mHrefs[1], "printCmdLine([[finger someone ]])");
QCOMPARE(stub.mHrefs[2], "printCmdLine([[tell someone ]])");
QCOMPARE(stub.mHints.size(), 3);
QCOMPARE(stub.mHints[0], "whisper someone");
QCOMPARE(stub.mHints[1], "finger someone");
QCOMPARE(stub.mHints[2], "tell someone");
}
};
#include "TMxpCustomElementTagHandlerTest.moc"
QTEST_MAIN(TMxpCustomElementTagHandlerTest)