|
|
|
@@ -3,6 +3,7 @@
|
|
|
|
|
#include "controllers/accounts/AccountController.hpp"
|
|
|
|
|
#include "controllers/completion/strategies/ClassicEmoteStrategy.hpp"
|
|
|
|
|
#include "controllers/completion/strategies/ClassicUserStrategy.hpp"
|
|
|
|
|
#include "controllers/completion/strategies/SmartEmoteStrategy.hpp"
|
|
|
|
|
#include "controllers/completion/strategies/Strategy.hpp"
|
|
|
|
|
#include "messages/Emote.hpp"
|
|
|
|
|
#include "mocks/BaseApplication.hpp"
|
|
|
|
@@ -84,7 +85,30 @@ public:
|
|
|
|
|
SeventvEmotes seventvEmotes;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
void containsRoughly(std::span<EmoteItem> span, const std::set<QString> &values)
|
|
|
|
|
{
|
|
|
|
|
for (const auto &v : values)
|
|
|
|
|
{
|
|
|
|
|
bool found = false;
|
|
|
|
|
for (const auto &actualValue : span)
|
|
|
|
|
{
|
|
|
|
|
if (actualValue.displayName == v)
|
|
|
|
|
{
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASSERT_TRUE(found) << v << " was not found in the span";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[nodiscard]] bool allEmoji(std::span<EmoteItem> span)
|
|
|
|
|
{
|
|
|
|
|
return std::ranges::all_of(span, [](const auto &it) {
|
|
|
|
|
return it.isEmoji && it.providerName == u"Emoji";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmotePtr namedEmote(const EmoteName &name)
|
|
|
|
|
{
|
|
|
|
@@ -104,7 +128,7 @@ void addEmote(EmoteMap &map, const QString &name)
|
|
|
|
|
map.insert(std::pair<EmoteName, EmotePtr>(eName, namedEmote(eName)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString DEFAULT_SETTINGS = R"!(
|
|
|
|
|
const QString DEFAULT_SETTINGS = R"!(
|
|
|
|
|
{
|
|
|
|
|
"accounts": {
|
|
|
|
|
"uid117166826": {
|
|
|
|
@@ -117,6 +141,8 @@ static QString DEFAULT_SETTINGS = R"!(
|
|
|
|
|
}
|
|
|
|
|
})!";
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
class InputCompletionTest : public ::testing::Test
|
|
|
|
|
{
|
|
|
|
|
protected:
|
|
|
|
@@ -164,6 +190,7 @@ private:
|
|
|
|
|
addEmote(*bttvEmotes, ":-)");
|
|
|
|
|
addEmote(*bttvEmotes, "B-)");
|
|
|
|
|
addEmote(*bttvEmotes, "Clap");
|
|
|
|
|
addEmote(*bttvEmotes, ":tf:");
|
|
|
|
|
this->mockApplication->bttvEmotes.setEmotes(std::move(bttvEmotes));
|
|
|
|
|
|
|
|
|
|
auto ffzEmotes = std::make_shared<EmoteMap>();
|
|
|
|
@@ -175,50 +202,56 @@ private:
|
|
|
|
|
auto seventvEmotes = std::make_shared<EmoteMap>();
|
|
|
|
|
addEmote(*seventvEmotes, "Clap");
|
|
|
|
|
addEmote(*seventvEmotes, "Clap2");
|
|
|
|
|
addEmote(*seventvEmotes, "pajaW");
|
|
|
|
|
addEmote(*seventvEmotes, "PAJAW");
|
|
|
|
|
this->mockApplication->seventvEmotes.setGlobalEmotes(
|
|
|
|
|
std::move(seventvEmotes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
auto queryClassicEmoteCompletion(const QString &fullQuery)
|
|
|
|
|
template <typename T>
|
|
|
|
|
auto queryEmoteCompletion(const QString &fullQuery)
|
|
|
|
|
{
|
|
|
|
|
EmoteSource source(this->channelPtr.get(),
|
|
|
|
|
std::make_unique<ClassicEmoteStrategy>());
|
|
|
|
|
EmoteSource source(this->channelPtr.get(), std::make_unique<T>());
|
|
|
|
|
source.update(fullQuery);
|
|
|
|
|
|
|
|
|
|
std::vector<EmoteItem> out(source.output());
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto queryClassicTabCompletion(const QString &fullQuery, bool isFirstWord)
|
|
|
|
|
template <typename T>
|
|
|
|
|
auto queryTabCompletion(const QString &fullQuery, bool isFirstWord)
|
|
|
|
|
{
|
|
|
|
|
EmoteSource source(this->channelPtr.get(),
|
|
|
|
|
std::make_unique<ClassicTabEmoteStrategy>());
|
|
|
|
|
EmoteSource source(this->channelPtr.get(), std::make_unique<T>());
|
|
|
|
|
source.update(fullQuery);
|
|
|
|
|
|
|
|
|
|
QStringList m;
|
|
|
|
|
source.addToStringList(m, 0, isFirstWord);
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void containsRoughly(std::span<EmoteItem> span, std::set<QString> values)
|
|
|
|
|
{
|
|
|
|
|
for (const auto &v : values)
|
|
|
|
|
auto queryClassicEmoteCompletion(const QString &fullQuery)
|
|
|
|
|
{
|
|
|
|
|
bool found = false;
|
|
|
|
|
for (const auto &actualValue : span)
|
|
|
|
|
{
|
|
|
|
|
if (actualValue.displayName == v)
|
|
|
|
|
{
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASSERT_TRUE(found) << v << " was not found in the span";
|
|
|
|
|
return queryEmoteCompletion<ClassicEmoteStrategy>(fullQuery);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto queryClassicTabCompletion(const QString &fullQuery, bool isFirstWord)
|
|
|
|
|
{
|
|
|
|
|
return queryTabCompletion<ClassicTabEmoteStrategy>(fullQuery,
|
|
|
|
|
isFirstWord);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto querySmartEmoteCompletion(const QString &fullQuery)
|
|
|
|
|
{
|
|
|
|
|
return queryEmoteCompletion<SmartEmoteStrategy>(fullQuery);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto querySmartTabCompletion(const QString &fullQuery, bool isFirstWord)
|
|
|
|
|
{
|
|
|
|
|
return queryTabCompletion<SmartTabEmoteStrategy>(fullQuery,
|
|
|
|
|
isFirstWord);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, ClassicEmoteNameFiltering)
|
|
|
|
|
{
|
|
|
|
@@ -230,9 +263,9 @@ TEST_F(InputCompletionTest, ClassicEmoteNameFiltering)
|
|
|
|
|
auto completion = queryClassicEmoteCompletion(":feels");
|
|
|
|
|
ASSERT_EQ(completion.size(), 3);
|
|
|
|
|
// all these matches are BTTV global emotes
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "FeelsBirthdayMan");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "FeelsBadMan");
|
|
|
|
|
ASSERT_EQ(completion[2].displayName, "FeelsGoodMan");
|
|
|
|
|
// these are in no specific order
|
|
|
|
|
containsRoughly(completion,
|
|
|
|
|
{"FeelsBirthdayMan", "FeelsBadMan", "FeelsGoodMan"});
|
|
|
|
|
|
|
|
|
|
completion = queryClassicEmoteCompletion(":)");
|
|
|
|
|
ASSERT_EQ(completion.size(), 3);
|
|
|
|
@@ -299,6 +332,30 @@ TEST_F(InputCompletionTest, ClassicEmoteProviderOrdering)
|
|
|
|
|
ASSERT_EQ(completion[4].providerName, "Emoji");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, ClassicEmoteCase)
|
|
|
|
|
{
|
|
|
|
|
auto completion = queryClassicEmoteCompletion(":pajaw");
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
// there's no order here
|
|
|
|
|
containsRoughly(completion, {"pajaW", "PAJAW"});
|
|
|
|
|
|
|
|
|
|
completion = queryClassicEmoteCompletion(":PA");
|
|
|
|
|
ASSERT_GT(completion.size(), 3);
|
|
|
|
|
containsRoughly({completion.begin(), 2}, {"pajaW", "PAJAW"});
|
|
|
|
|
containsRoughly({completion.begin() + 2, completion.end()}, {"parking"});
|
|
|
|
|
ASSERT_TRUE(allEmoji({completion.begin() + 2, completion.end()}));
|
|
|
|
|
|
|
|
|
|
completion = queryClassicEmoteCompletion(":Pajaw");
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
containsRoughly(completion, {"pajaW", "PAJAW"});
|
|
|
|
|
|
|
|
|
|
completion = queryClassicEmoteCompletion(":NOTHING");
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
|
|
|
|
|
completion = queryClassicEmoteCompletion(":nothing");
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, ClassicTabCompletionEmote)
|
|
|
|
|
{
|
|
|
|
|
auto completion = queryClassicTabCompletion(":feels", false);
|
|
|
|
@@ -328,7 +385,13 @@ TEST_F(InputCompletionTest, ClassicTabCompletionEmote)
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, ClassicTabCompletionEmoji)
|
|
|
|
|
{
|
|
|
|
|
auto completion = queryClassicTabCompletion(":cla", false);
|
|
|
|
|
auto completion = queryClassicTabCompletion(":tf", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
|
|
|
|
|
completion = queryClassicTabCompletion(":)", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
|
|
|
|
|
completion = queryClassicTabCompletion(":cla", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 8);
|
|
|
|
|
ASSERT_EQ(completion[0], ":clap: ");
|
|
|
|
|
ASSERT_EQ(completion[1], ":clap_tone1: ");
|
|
|
|
@@ -339,3 +402,177 @@ TEST_F(InputCompletionTest, ClassicTabCompletionEmoji)
|
|
|
|
|
ASSERT_EQ(completion[6], ":clapper: ");
|
|
|
|
|
ASSERT_EQ(completion[7], ":classical_building: ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, ClassicTabCompletionCase)
|
|
|
|
|
{
|
|
|
|
|
auto completion = queryClassicTabCompletion("pajaw", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
ASSERT_EQ(completion[0], "pajaW ");
|
|
|
|
|
ASSERT_EQ(completion[1], "PAJAW ");
|
|
|
|
|
|
|
|
|
|
completion = queryClassicTabCompletion("PA", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
ASSERT_EQ(completion[0], "pajaW ");
|
|
|
|
|
ASSERT_EQ(completion[1], "PAJAW ");
|
|
|
|
|
|
|
|
|
|
completion = queryClassicTabCompletion("Pajaw", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
ASSERT_EQ(completion[0], "pajaW ");
|
|
|
|
|
ASSERT_EQ(completion[1], "PAJAW ");
|
|
|
|
|
|
|
|
|
|
completion = queryClassicTabCompletion("NOTHING", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
|
|
|
|
|
completion = queryClassicTabCompletion("nothing", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, SmartEmoteNameFiltering)
|
|
|
|
|
{
|
|
|
|
|
auto completion = querySmartEmoteCompletion(":feels");
|
|
|
|
|
ASSERT_EQ(completion.size(), 3);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "FeelsBadMan");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "FeelsGoodMan");
|
|
|
|
|
ASSERT_EQ(completion[2].displayName, "FeelsBirthdayMan");
|
|
|
|
|
|
|
|
|
|
completion = querySmartEmoteCompletion(":)");
|
|
|
|
|
ASSERT_EQ(completion.size(), 3);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, ":)");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, ":-)");
|
|
|
|
|
ASSERT_EQ(completion[2].displayName, "B-)");
|
|
|
|
|
|
|
|
|
|
completion = querySmartEmoteCompletion(":cat");
|
|
|
|
|
ASSERT_TRUE(completion.size() >= 4);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "cat");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "cat2");
|
|
|
|
|
ASSERT_EQ(completion[2].displayName, "CatBag");
|
|
|
|
|
ASSERT_EQ(completion[3].displayName, "joy_cat");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, SmartEmoteExactNameMatching)
|
|
|
|
|
{
|
|
|
|
|
auto completion = querySmartEmoteCompletion(":sal");
|
|
|
|
|
ASSERT_TRUE(completion.size() >= 4);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "salt");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "SaltyCorn");
|
|
|
|
|
ASSERT_EQ(completion[2].displayName, "green_salad");
|
|
|
|
|
ASSERT_EQ(completion[3].displayName, "saluting_face");
|
|
|
|
|
|
|
|
|
|
completion = querySmartEmoteCompletion(":salt");
|
|
|
|
|
ASSERT_TRUE(completion.size() >= 2);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "salt");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "SaltyCorn");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, SmartEmoteProviderOrdering)
|
|
|
|
|
{
|
|
|
|
|
auto completion = querySmartEmoteCompletion(":clap");
|
|
|
|
|
ASSERT_TRUE(completion.size() >= 6);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "clap");
|
|
|
|
|
ASSERT_EQ(completion[0].providerName, "Emoji");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "Clap");
|
|
|
|
|
ASSERT_EQ(completion[1].providerName, "Global BetterTTV");
|
|
|
|
|
ASSERT_EQ(completion[2].displayName, "Clap");
|
|
|
|
|
ASSERT_EQ(completion[2].providerName, "Global 7TV");
|
|
|
|
|
ASSERT_EQ(completion[3].displayName, "Clap2");
|
|
|
|
|
ASSERT_EQ(completion[3].providerName, "Global 7TV");
|
|
|
|
|
ASSERT_EQ(completion[4].displayName, "clapper");
|
|
|
|
|
ASSERT_EQ(completion[4].providerName, "Emoji");
|
|
|
|
|
ASSERT_EQ(completion[5].displayName, "clap_tone1");
|
|
|
|
|
ASSERT_EQ(completion[5].providerName, "Emoji");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, SmartEmoteCase)
|
|
|
|
|
{
|
|
|
|
|
auto completion = querySmartEmoteCompletion(":pajaw");
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "pajaW");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "PAJAW");
|
|
|
|
|
|
|
|
|
|
completion = querySmartEmoteCompletion(":PA");
|
|
|
|
|
ASSERT_EQ(completion.size(), 1);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "PAJAW");
|
|
|
|
|
|
|
|
|
|
completion = querySmartEmoteCompletion(":Pajaw");
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
ASSERT_EQ(completion[0].displayName, "PAJAW");
|
|
|
|
|
ASSERT_EQ(completion[1].displayName, "pajaW");
|
|
|
|
|
|
|
|
|
|
completion = querySmartEmoteCompletion(":NOTHING");
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
|
|
|
|
|
completion = querySmartEmoteCompletion(":nothing");
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, SmartTabCompletionEmote)
|
|
|
|
|
{
|
|
|
|
|
auto completion = querySmartTabCompletion(":feels", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0); // : prefix matters here
|
|
|
|
|
|
|
|
|
|
// no : prefix defaults to emote completion
|
|
|
|
|
completion = querySmartTabCompletion("feels", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 3);
|
|
|
|
|
ASSERT_EQ(completion[0], "FeelsBadMan ");
|
|
|
|
|
ASSERT_EQ(completion[1], "FeelsGoodMan ");
|
|
|
|
|
ASSERT_EQ(completion[2], "FeelsBirthdayMan ");
|
|
|
|
|
|
|
|
|
|
// no : prefix, emote completion. Duplicate Clap should be removed
|
|
|
|
|
completion = querySmartTabCompletion("cla", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 3);
|
|
|
|
|
ASSERT_EQ(completion[0], "Clap ");
|
|
|
|
|
ASSERT_EQ(completion[1], "Clap ");
|
|
|
|
|
ASSERT_EQ(completion[2], "Clap2 ");
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion("peepoHappy", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion("Aware", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 1);
|
|
|
|
|
ASSERT_EQ(completion[0], "Aware ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, SmartTabCompletionEmoji)
|
|
|
|
|
{
|
|
|
|
|
auto completion = querySmartTabCompletion(":tf", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
// ASSERT_EQ(completion[0], ":tf: ");
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion(":)", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
// ASSERT_EQ(completion[0], ":) ");
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion(":cla", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 8);
|
|
|
|
|
ASSERT_EQ(completion[0], ":clap: ");
|
|
|
|
|
ASSERT_EQ(completion[1], ":clapper: ");
|
|
|
|
|
ASSERT_EQ(completion[2], ":clap_tone1: ");
|
|
|
|
|
ASSERT_EQ(completion[3], ":clap_tone2: ");
|
|
|
|
|
ASSERT_EQ(completion[4], ":clap_tone3: ");
|
|
|
|
|
ASSERT_EQ(completion[5], ":clap_tone4: ");
|
|
|
|
|
ASSERT_EQ(completion[6], ":clap_tone5: ");
|
|
|
|
|
ASSERT_EQ(completion[7], ":classical_building: ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_F(InputCompletionTest, SmartTabCompletionCase)
|
|
|
|
|
{
|
|
|
|
|
auto completion = querySmartTabCompletion("pajaw", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
ASSERT_EQ(completion[0], "pajaW ");
|
|
|
|
|
ASSERT_EQ(completion[1], "PAJAW ");
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion("PA", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 1);
|
|
|
|
|
ASSERT_EQ(completion[0], "PAJAW ");
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion("Pajaw", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 2);
|
|
|
|
|
ASSERT_EQ(completion[0], "PAJAW ");
|
|
|
|
|
ASSERT_EQ(completion[1], "pajaW ");
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion("NOTHING", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
|
|
|
|
|
completion = querySmartTabCompletion("nothing", false);
|
|
|
|
|
ASSERT_EQ(completion.size(), 0);
|
|
|
|
|
}
|
|
|
|
|