test: add snapshot tests for MessageBuilder (#5598)

This commit is contained in:
nerix
2024-10-13 12:38:10 +02:00
committed by GitHub
parent 3e2116629a
commit d9453313b3
90 changed files with 14661 additions and 218 deletions
+27
View File
@@ -1,5 +1,6 @@
#include "providers/ffz/FfzBadges.hpp"
#include "Application.hpp"
#include "common/network/NetworkRequest.hpp"
#include "common/network/NetworkResult.hpp"
#include "messages/Emote.hpp"
@@ -109,4 +110,30 @@ void FfzBadges::load()
.execute();
}
void FfzBadges::registerBadge(int badgeID, Badge badge)
{
assert(getApp()->isTest());
std::unique_lock lock(this->mutex_);
this->badges.emplace(badgeID, std::move(badge));
}
void FfzBadges::assignBadgeToUser(const UserId &userID, int badgeID)
{
assert(getApp()->isTest());
std::unique_lock lock(this->mutex_);
auto it = this->userBadges.find(userID.string);
if (it != this->userBadges.end())
{
it->second.emplace(badgeID);
}
else
{
this->userBadges.emplace(userID.string, std::set{badgeID});
}
}
} // namespace chatterino
+3
View File
@@ -30,6 +30,9 @@ public:
std::vector<Badge> getUserBadges(const UserId &id);
std::optional<Badge> getBadge(int badgeID) const;
void registerBadge(int badgeID, Badge badge);
void assignBadgeToUser(const UserId &userID, int badgeID);
void load();
private:
+14 -9
View File
@@ -632,15 +632,6 @@ std::vector<MessagePtr> parsePrivMessage(Channel *channel,
builder.triggerHighlights();
}
if (message->tags().contains(u"pinned-chat-paid-amount"_s))
{
auto ptr = MessageBuilder::buildHypeChatMessage(message);
if (ptr)
{
builtMessages.emplace_back(std::move(ptr));
}
}
return builtMessages;
}
@@ -676,6 +667,11 @@ std::vector<MessagePtr> IrcMessageHandler::parseMessageWithReply(
QString content = privMsg->content();
int messageOffset = stripLeadingReplyMention(privMsg->tags(), content);
MessageParseArgs args;
auto tags = privMsg->tags();
if (const auto it = tags.find("custom-reward-id"); it != tags.end())
{
args.channelPointRewardId = it.value().toString();
}
MessageBuilder builder(channel, message, args, content,
privMsg->isAction());
builder.setMessageOffset(messageOffset);
@@ -688,6 +684,15 @@ std::vector<MessagePtr> IrcMessageHandler::parseMessageWithReply(
builder.triggerHighlights();
}
if (message->tags().contains(u"pinned-chat-paid-amount"_s))
{
auto ptr = MessageBuilder::buildHypeChatMessage(privMsg);
if (ptr)
{
builtMessages.emplace_back(std::move(ptr));
}
}
return builtMessages;
}
+17
View File
@@ -174,6 +174,17 @@ void TwitchAccount::unblockUser(const QString &userId, const QObject *caller,
std::move(onFailure));
}
void TwitchAccount::blockUserLocally(const QString &userID)
{
assertInGuiThread();
assert(getApp()->isTest());
TwitchUser blockedUser;
blockedUser.id = userID;
this->ignores_.insert(blockedUser);
this->ignoresUserIds_.insert(blockedUser.id);
}
const std::unordered_set<TwitchUser> &TwitchAccount::blocks() const
{
assertInGuiThread();
@@ -336,6 +347,12 @@ SharedAccessGuard<std::shared_ptr<const EmoteMap>> TwitchAccount::accessEmotes()
return this->emotes_.accessConst();
}
void TwitchAccount::setEmotes(std::shared_ptr<const EmoteMap> emotes)
{
assert(getApp()->isTest());
*this->emotes_.access() = std::move(emotes);
}
std::optional<EmotePtr> TwitchAccount::twitchEmote(const EmoteName &name) const
{
auto emotes = this->emotes_.accessConst();
+9 -2
View File
@@ -71,6 +71,8 @@ public:
std::function<void()> onSuccess,
std::function<void()> onFailure);
void blockUserLocally(const QString &userID);
[[nodiscard]] const std::unordered_set<TwitchUser> &blocks() const;
[[nodiscard]] const std::unordered_set<QString> &blockedUserIds() const;
@@ -83,16 +85,21 @@ public:
/// Returns true if the account has access to the given emote set
bool hasEmoteSet(const EmoteSetId &id) const;
/// Return a map of emote sets the account has access to
/// Returns a map of emote sets the account has access to
///
/// Key being the emote set ID, and contents being information about the emote set
/// and the emotes contained in the emote set
SharedAccessGuard<std::shared_ptr<const TwitchEmoteSetMap>>
accessEmoteSets() const;
/// Return a map of emotes the account has access to
/// Returns a map of emotes the account has access to
SharedAccessGuard<std::shared_ptr<const EmoteMap>> accessEmotes() const;
/// Sets the emotes this account has access to
///
/// This should only be used in tests.
void setEmotes(std::shared_ptr<const EmoteMap> emotes);
/// Return the emote by emote name if the account has access to the emote
std::optional<EmotePtr> twitchEmote(const EmoteName &name) const;
+1 -1
View File
@@ -45,10 +45,10 @@ public:
void loadTwitchBadges();
private:
/// Loads the badges shipped with Chatterino (twitch-badges.json)
void loadLocalBadges();
private:
void loaded();
void loadEmoteImage(const QString &name, const ImagePtr &image,
BadgeIconCallback &&callback);
+116 -88
View File
@@ -462,6 +462,14 @@ void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward)
}
}
void TwitchChannel::addKnownChannelPointReward(const ChannelPointReward &reward)
{
assert(getApp()->isTest());
auto channelPointRewards = this->channelPointRewards_.access();
channelPointRewards->try_emplace(reward.id, reward);
}
bool TwitchChannel::isChannelPointRewardKnown(const QString &rewardId)
{
const auto &pointRewards = this->channelPointRewards_.accessConst();
@@ -1560,7 +1568,7 @@ void TwitchChannel::refreshBadges()
getHelix()->getChannelBadges(
this->roomId(),
// successCallback
[this, weak = weakOf<Channel>(this)](auto channelBadges) {
[this, weak = weakOf<Channel>(this)](const auto &channelBadges) {
auto shared = weak.lock();
if (!shared)
{
@@ -1568,31 +1576,7 @@ void TwitchChannel::refreshBadges()
return;
}
auto badgeSets = this->badgeSets_.access();
for (const auto &badgeSet : channelBadges.badgeSets)
{
const auto &setID = badgeSet.setID;
for (const auto &version : badgeSet.versions)
{
auto emote = Emote{
.name = EmoteName{},
.images =
ImageSet{
Image::fromUrl(version.imageURL1x, 1,
BASE_BADGE_SIZE),
Image::fromUrl(version.imageURL2x, .5,
BASE_BADGE_SIZE * 2),
Image::fromUrl(version.imageURL4x, .25,
BASE_BADGE_SIZE * 4),
},
.tooltip = Tooltip{version.title},
.homePage = version.clickURL,
};
(*badgeSets)[setID][version.id] =
std::make_shared<Emote>(emote);
}
}
this->addTwitchBadgeSets(channelBadges);
},
// failureCallback
[this, weak = weakOf<Channel>(this)](auto error, auto message) {
@@ -1623,6 +1607,33 @@ void TwitchChannel::refreshBadges()
});
}
void TwitchChannel::addTwitchBadgeSets(const HelixChannelBadges &channelBadges)
{
auto badgeSets = this->badgeSets_.access();
for (const auto &badgeSet : channelBadges.badgeSets)
{
const auto &setID = badgeSet.setID;
for (const auto &version : badgeSet.versions)
{
auto emote = Emote{
.name = EmoteName{},
.images =
ImageSet{
Image::fromUrl(version.imageURL1x, 1, BASE_BADGE_SIZE),
Image::fromUrl(version.imageURL2x, .5,
BASE_BADGE_SIZE * 2),
Image::fromUrl(version.imageURL4x, .25,
BASE_BADGE_SIZE * 4),
},
.tooltip = Tooltip{version.title},
.homePage = version.clickURL,
};
(*badgeSets)[setID][version.id] = std::make_shared<Emote>(emote);
}
}
}
void TwitchChannel::refreshCheerEmotes()
{
getHelix()->getCheermotes(
@@ -1635,74 +1646,75 @@ void TwitchChannel::refreshCheerEmotes()
return;
}
std::vector<CheerEmoteSet> emoteSets;
for (const auto &set : cheermoteSets)
{
auto cheerEmoteSet = CheerEmoteSet();
cheerEmoteSet.regex = QRegularExpression(
"^" + set.prefix + "([1-9][0-9]*)$",
QRegularExpression::CaseInsensitiveOption);
for (const auto &tier : set.tiers)
{
CheerEmote cheerEmote;
cheerEmote.color = QColor(tier.color);
cheerEmote.minBits = tier.minBits;
cheerEmote.regex = cheerEmoteSet.regex;
// TODO(pajlada): We currently hardcode dark here :|
// We will continue to do so for now since we haven't had to
// solve that anywhere else
// Combine the prefix (e.g. BibleThump) with the tier (1, 100 etc.)
auto emoteTooltip =
set.prefix + tier.id + "<br>Twitch Cheer Emote";
auto makeImageSet = [](const HelixCheermoteImage &image) {
return ImageSet{
Image::fromUrl(image.imageURL1x, 1.0,
BASE_BADGE_SIZE),
Image::fromUrl(image.imageURL2x, 0.5,
BASE_BADGE_SIZE * 2),
Image::fromUrl(image.imageURL4x, 0.25,
BASE_BADGE_SIZE * 4),
};
};
cheerEmote.animatedEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{"cheer emote"},
.images = makeImageSet(tier.darkAnimated),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});
cheerEmote.staticEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{"cheer emote"},
.images = makeImageSet(tier.darkStatic),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});
cheerEmoteSet.cheerEmotes.emplace_back(
std::move(cheerEmote));
}
// Sort cheermotes by cost
std::sort(cheerEmoteSet.cheerEmotes.begin(),
cheerEmoteSet.cheerEmotes.end(),
[](const auto &lhs, const auto &rhs) {
return lhs.minBits > rhs.minBits;
});
emoteSets.emplace_back(std::move(cheerEmoteSet));
}
*this->cheerEmoteSets_.access() = std::move(emoteSets);
this->setCheerEmoteSets(cheermoteSets);
},
[] {
// Failure
});
}
void TwitchChannel::setCheerEmoteSets(
const std::vector<HelixCheermoteSet> &cheermoteSets)
{
std::vector<CheerEmoteSet> emoteSets;
for (const auto &set : cheermoteSets)
{
auto cheerEmoteSet = CheerEmoteSet();
cheerEmoteSet.regex =
QRegularExpression("^" + set.prefix + "([1-9][0-9]*)$",
QRegularExpression::CaseInsensitiveOption);
for (const auto &tier : set.tiers)
{
CheerEmote cheerEmote;
cheerEmote.color = QColor(tier.color);
cheerEmote.minBits = tier.minBits;
cheerEmote.regex = cheerEmoteSet.regex;
// TODO(pajlada): We currently hardcode dark here :|
// We will continue to do so for now since we haven't had to
// solve that anywhere else
// Combine the prefix (e.g. BibleThump) with the tier (1, 100 etc.)
auto emoteTooltip = set.prefix + tier.id + "<br>Twitch Cheer Emote";
auto makeImageSet = [](const HelixCheermoteImage &image) {
return ImageSet{
Image::fromUrl(image.imageURL1x, 1.0, BASE_BADGE_SIZE),
Image::fromUrl(image.imageURL2x, 0.5, BASE_BADGE_SIZE * 2),
Image::fromUrl(image.imageURL4x, 0.25, BASE_BADGE_SIZE * 4),
};
};
cheerEmote.animatedEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{u"cheer emote"_s},
.images = makeImageSet(tier.darkAnimated),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});
cheerEmote.staticEmote = std::make_shared<Emote>(Emote{
.name = EmoteName{u"cheer emote"_s},
.images = makeImageSet(tier.darkStatic),
.tooltip = Tooltip{emoteTooltip},
.homePage = Url{},
});
cheerEmoteSet.cheerEmotes.emplace_back(std::move(cheerEmote));
}
// Sort cheermotes by cost
std::sort(cheerEmoteSet.cheerEmotes.begin(),
cheerEmoteSet.cheerEmotes.end(),
[](const auto &lhs, const auto &rhs) {
return lhs.minBits > rhs.minBits;
});
emoteSets.emplace_back(std::move(cheerEmoteSet));
}
*this->cheerEmoteSets_.access() = std::move(emoteSets);
}
void TwitchChannel::createClip()
{
if (!this->isLive())
@@ -1859,6 +1871,12 @@ std::vector<FfzBadges::Badge> TwitchChannel::ffzChannelBadges(
return badges;
}
void TwitchChannel::setFfzChannelBadges(FfzChannelBadgeMap map)
{
this->tgFfzChannelBadges_.guard();
this->ffzChannelBadges_ = std::move(map);
}
std::optional<EmotePtr> TwitchChannel::ffzCustomModBadge() const
{
return this->ffzCustomModBadge_.get();
@@ -1869,6 +1887,16 @@ std::optional<EmotePtr> TwitchChannel::ffzCustomVipBadge() const
return this->ffzCustomVipBadge_.get();
}
void TwitchChannel::setFfzCustomModBadge(std::optional<EmotePtr> badge)
{
this->ffzCustomModBadge_.set(std::move(badge));
}
void TwitchChannel::setFfzCustomVipBadge(std::optional<EmotePtr> badge)
{
this->ffzCustomVipBadge_.set(std::move(badge));
}
std::optional<CheerEmote> TwitchChannel::cheerEmote(const QString &string) const
{
auto sets = this->cheerEmoteSets_.access();
+16
View File
@@ -25,6 +25,8 @@
#include <optional>
#include <unordered_map>
class TestMessageBuilderP;
namespace chatterino {
enum class HighlightState;
@@ -51,6 +53,9 @@ struct ChannelPointReward;
class MessageThread;
struct CheerEmoteSet;
struct HelixStream;
struct HelixCheermoteSet;
struct HelixGlobalBadges;
using HelixChannelBadges = HelixGlobalBadges;
class TwitchIrcServer;
@@ -195,9 +200,15 @@ public:
* Returns a list of channel-specific FrankerFaceZ badges for the given user
*/
std::vector<FfzBadges::Badge> ffzChannelBadges(const QString &userID) const;
void setFfzChannelBadges(FfzChannelBadgeMap map);
void setFfzCustomModBadge(std::optional<EmotePtr> badge);
void setFfzCustomVipBadge(std::optional<EmotePtr> badge);
void addTwitchBadgeSets(const HelixChannelBadges &channelBadges);
// Cheers
std::optional<CheerEmote> cheerEmote(const QString &string) const;
void setCheerEmoteSets(const std::vector<HelixCheermoteSet> &cheermoteSets);
// Replies
/**
@@ -243,6 +254,10 @@ public:
* This will look at queued up partial messages, and if one is found it will add the queued up partial messages fully hydrated.
**/
void addChannelPointReward(const ChannelPointReward &reward);
/// Adds @a reward to the known rewards
///
/// Unlike in #addChannelPointReward(), no message will be sent.
void addKnownChannelPointReward(const ChannelPointReward &reward);
bool isChannelPointRewardKnown(const QString &rewardId);
std::optional<ChannelPointReward> channelPointReward(
const QString &rewardId) const;
@@ -449,6 +464,7 @@ private:
friend class MessageBuilder;
friend class IrcMessageHandler;
friend class Commands_E2E_Test;
friend class ::TestMessageBuilderP;
};
} // namespace chatterino