mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-11-11 23:14:22 +09:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
936bc731d6 | ||
|
|
6cd340bcf7 | ||
|
|
56e8d6d974 | ||
|
|
4cd6aa90ee | ||
|
|
00b965fc5b | ||
|
|
9fafb29c6d | ||
|
|
ddbb7656fa | ||
|
|
7da3102ee2 | ||
|
|
cbee1bf8a8 | ||
|
|
c8046e285d | ||
|
|
02e5dc6799 | ||
|
|
db828d4c42 | ||
|
|
728c0ce617 | ||
|
|
e3bb5bdb78 | ||
|
|
2551d9f26b | ||
|
|
8f1c575fad | ||
|
|
106fbedc2c |
No files matched your search
@@ -1062,6 +1062,7 @@ jobs:
|
||||
- ConnectionTest
|
||||
- OpenSSLLibraryTest
|
||||
- GatewayServiceImportTest
|
||||
- PagesUiTest
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -1279,6 +1280,7 @@ jobs:
|
||||
- ConnectionTest
|
||||
- OpenSSLLibraryTest
|
||||
- GatewayServiceImportTest
|
||||
- PagesUiTest
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
@@ -1398,6 +1400,7 @@ jobs:
|
||||
- ConnectionTest
|
||||
- OpenSSLLibraryTest
|
||||
- GatewayServiceImportTest
|
||||
- PagesUiTest
|
||||
|
||||
runs-on: macos-15
|
||||
|
||||
|
||||
@@ -323,6 +323,34 @@ if(NOT ANDROID)
|
||||
)
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
qt6_add_resources(PAGES_UI_QRC
|
||||
${CLIENT_QML_QRC_FILE}
|
||||
../images/images.qrc
|
||||
)
|
||||
|
||||
add_executable(test_pages_ui
|
||||
pagesUi/main.cpp
|
||||
pagesUi/pagesUiTestFixture.cpp
|
||||
pagesUi/pagesUiTestFixture.h
|
||||
pagesUi/testPageSetupWizardConfigSource.cpp
|
||||
pagesUi/testPageHome.cpp
|
||||
${PAGES_UI_QRC}
|
||||
)
|
||||
|
||||
target_link_libraries(test_pages_ui PRIVATE
|
||||
Qt6::Test
|
||||
test_common
|
||||
SortFilterProxyModel
|
||||
)
|
||||
|
||||
if(COMMAND qt_import_qml_plugins)
|
||||
qt_import_qml_plugins(test_pages_ui)
|
||||
endif()
|
||||
|
||||
if(COMMAND qt_finalize_executable)
|
||||
qt_finalize_executable(test_pages_ui)
|
||||
endif()
|
||||
endif() # NOT ANDROID
|
||||
|
||||
if(ANDROID)
|
||||
@@ -492,6 +520,8 @@ if(NOT ANDROID)
|
||||
add_test(NAME ConnectionTest COMMAND test_connection)
|
||||
add_test(NAME OpenSSLLibraryTest COMMAND test_openssl_library)
|
||||
add_test(NAME LogsControllerTest COMMAND test_logs_controller)
|
||||
|
||||
add_test(NAME PagesUiTest COMMAND test_pages_ui)
|
||||
endif()
|
||||
|
||||
# Android tests are executed on-device via androidtestrunner in CI (see .github/workflows/tests.yml).
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <QApplication>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickStyle>
|
||||
#include <QUrl>
|
||||
#include <QTest>
|
||||
|
||||
#include "ui/models/containerProps.h"
|
||||
#include "ui/utils/pageEnum.h"
|
||||
#include "core/utils/protocolEnum.h"
|
||||
|
||||
int runPageSetupWizardConfigSourceTests(int argc, char *argv[]);
|
||||
int runPageHomeTests(int argc, char *argv[]);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QQuickStyle::setStyle(QStringLiteral("Basic"));
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
PageLoader::declareQmlPageEnum();
|
||||
|
||||
qmlRegisterUncreatableMetaObject(amnezia::ProtocolEnumNS::staticMetaObject, "ProtocolEnum", 1, 0, "ProtocolEnum", QStringLiteral("Error: only enums"));
|
||||
|
||||
static ContainerProps containerProps;
|
||||
|
||||
qmlRegisterSingletonInstance("ContainerProps", 1, 0, "ContainerProps", &containerProps);
|
||||
qmlRegisterSingletonType(QUrl(QStringLiteral("qrc:/ui/qml/Filters/ContainersModelFilters.qml")), "ContainersModelFilters", 1, 0, "ContainersModelFilters");
|
||||
|
||||
int result = 0;
|
||||
|
||||
result |= runPageSetupWizardConfigSourceTests(argc, argv);
|
||||
result |= runPageHomeTests(argc, argv);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
#include "pagesUiTestFixture.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QElapsedTimer>
|
||||
#include <QEvent>
|
||||
#include <QJSValue>
|
||||
#include <QObject>
|
||||
#include <QQuickStyle>
|
||||
#include <QSet>
|
||||
#include <QUuid>
|
||||
#include <QVariant>
|
||||
|
||||
#include <vpnConnection.h>
|
||||
|
||||
#include "ui/utils/pageEnum.h"
|
||||
|
||||
bool PagesUiTestFixture::initialize(QObject *parent)
|
||||
{
|
||||
m_settings = new SecureQSettings("AmneziaVPN-PagesUiTest-" + QUuid::createUuid().toString(), "amnezia-client",
|
||||
parent, false);
|
||||
|
||||
m_engine = new QQmlApplicationEngine(parent);
|
||||
m_engine->addImportPath(QStringLiteral("qrc:/ui/qml/Modules/"));
|
||||
m_engine->rootContext()->setContextProperty("IsMacOsNeBuild", false);
|
||||
m_engine->rootContext()->setContextProperty("IsPlayBuild", false);
|
||||
|
||||
auto vpnConnection = QSharedPointer<VpnConnection>::create(nullptr, nullptr);
|
||||
|
||||
m_core = new TestCoreController(vpnConnection, m_settings, m_engine, parent);
|
||||
|
||||
m_pageController = m_core->pageController();
|
||||
|
||||
return m_pageController != nullptr;
|
||||
}
|
||||
|
||||
void PagesUiTestFixture::cleanup()
|
||||
{
|
||||
closePage();
|
||||
|
||||
if (m_settings) {
|
||||
m_settings->clearSettings();
|
||||
}
|
||||
|
||||
delete m_core;
|
||||
m_core = nullptr;
|
||||
m_pageController = nullptr;
|
||||
|
||||
delete m_engine;
|
||||
m_engine = nullptr;
|
||||
|
||||
delete m_settings;
|
||||
m_settings = nullptr;
|
||||
}
|
||||
|
||||
bool PagesUiTestFixture::loadPage(const QString &pageName)
|
||||
{
|
||||
closePage();
|
||||
|
||||
const QByteArray hostQml = QStringLiteral("import QtQuick\n"
|
||||
"import QtQuick.Controls\n"
|
||||
"import \"../Config\"\n"
|
||||
"\n"
|
||||
"ApplicationWindow {\n"
|
||||
" width: 420\n"
|
||||
" height: 800\n"
|
||||
" visible: true\n"
|
||||
" property var dialogs: Dialogs\n"
|
||||
"\n"
|
||||
" Loader {\n"
|
||||
" objectName: \"pageLoader\"\n"
|
||||
" anchors.fill: parent\n"
|
||||
" source: \"qrc:/ui/qml/Pages2/%1.qml\"\n"
|
||||
" }\n"
|
||||
"}\n")
|
||||
.arg(pageName)
|
||||
.toUtf8();
|
||||
|
||||
m_engine->loadData(hostQml, QUrl(QStringLiteral("qrc:/ui/qml/Pages2/pagesUiTestHost.qml")));
|
||||
|
||||
if (m_engine->rootObjects().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window = qobject_cast<QQuickWindow *>(m_engine->rootObjects().last());
|
||||
|
||||
if (!m_window || !QTest::qWaitForWindowExposed(m_window)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window->requestActivate();
|
||||
QTest::qWaitForWindowActive(m_window);
|
||||
|
||||
QObject *loader = m_window->findChild<QObject *>(QStringLiteral("pageLoader"));
|
||||
|
||||
if (!loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!QTest::qWaitFor([loader] { return loader->property("status").toInt() == 1; }, 5000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_page = qvariant_cast<QQuickItem *>(loader->property("item"));
|
||||
|
||||
return m_page != nullptr;
|
||||
}
|
||||
|
||||
bool PagesUiTestFixture::loadPageFromStartPage(PageLoader::PageEnum page)
|
||||
{
|
||||
if (!m_pageController || !m_pageController->isStartPageVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the actual application start page
|
||||
if (!loadPage(QStringLiteral("PageStart"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Navigate through PageController
|
||||
emit m_pageController->goToPage(page, false);
|
||||
|
||||
const QString pagePath = m_pageController->getPagePath(page);
|
||||
QQuickItem *targetPage = nullptr;
|
||||
|
||||
const bool pageLoaded = QTest::qWaitFor(
|
||||
[this, &targetPage, &pagePath]() {
|
||||
targetPage = item(QStringLiteral("objectName"), pagePath, m_page);
|
||||
|
||||
return targetPage && targetPage->isVisible();
|
||||
},
|
||||
5000);
|
||||
|
||||
if (!pageLoaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_page = targetPage;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PagesUiTestFixture::closePage()
|
||||
{
|
||||
m_page = nullptr;
|
||||
|
||||
if (!m_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_window->close();
|
||||
m_window->deleteLater();
|
||||
m_window = nullptr;
|
||||
|
||||
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
|
||||
}
|
||||
|
||||
static void collectFromObject(QObject *object, QList<QQuickItem *> &result, QSet<QObject *> &seen)
|
||||
{
|
||||
if (!object || seen.contains(object)) {
|
||||
return;
|
||||
}
|
||||
|
||||
seen.insert(object);
|
||||
|
||||
if (auto *item = qobject_cast<QQuickItem *>(object)) {
|
||||
result.append(item);
|
||||
|
||||
for (QQuickItem *child : item->childItems()) {
|
||||
collectFromObject(child, result, seen);
|
||||
}
|
||||
}
|
||||
|
||||
const QObjectList objectChildren = object->children();
|
||||
for (QObject *child : objectChildren) {
|
||||
collectFromObject(child, result, seen);
|
||||
}
|
||||
}
|
||||
|
||||
void PagesUiTestFixture::collectItems(QQuickItem *root, QList<QQuickItem *> &result)
|
||||
{
|
||||
QSet<QObject *> seen;
|
||||
collectFromObject(root, result, seen);
|
||||
}
|
||||
|
||||
QList<QQuickItem *> PagesUiTestFixture::items(QQuickItem *root) const
|
||||
{
|
||||
QList<QQuickItem *> result;
|
||||
QSet<QObject *> seen;
|
||||
|
||||
if (root) {
|
||||
collectFromObject(root, result, seen);
|
||||
return result;
|
||||
}
|
||||
|
||||
collectFromObject(m_page, result, seen);
|
||||
|
||||
if (m_window && m_window->contentItem()) {
|
||||
collectFromObject(m_window->contentItem(), result, seen);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool isShown(QQuickItem *item)
|
||||
{
|
||||
if (!item || !item->isVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (QObject *object = item; object; object = object->parent()) {
|
||||
if (object->inherits("QQuickPopup")) {
|
||||
return object->property("visible").toBool();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PagesUiTestFixture::isQmlType(const QObject *object, const QString &qmlType)
|
||||
{
|
||||
if (!object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString className = QString::fromLatin1(object->metaObject()->className());
|
||||
return className == qmlType || className.startsWith(qmlType + QLatin1Char('_'));
|
||||
}
|
||||
|
||||
QList<QQuickItem *> PagesUiTestFixture::itemsOfType(const QString &qmlType, QQuickItem *root) const
|
||||
{
|
||||
QList<QQuickItem *> result;
|
||||
|
||||
for (QQuickItem *candidate : items(root)) {
|
||||
if (isQmlType(candidate, qmlType)) {
|
||||
result.append(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QQuickItem *PagesUiTestFixture::item(const QString &property, const QVariant &value, QQuickItem *root) const
|
||||
{
|
||||
for (QQuickItem *candidate : items(root)) {
|
||||
const QVariant candidateValue = candidate->property(property.toUtf8().constData());
|
||||
|
||||
if (candidateValue.isValid() && candidateValue == value) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QQuickItem *PagesUiTestFixture::visibleItem(const QString &property, const QVariant &value, QQuickItem *root) const
|
||||
{
|
||||
for (QQuickItem *candidate : items(root)) {
|
||||
const QVariant candidateValue = candidate->property(property.toUtf8().constData());
|
||||
|
||||
if (isShown(candidate) && candidateValue.isValid() && candidateValue == value) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QQuickItem *PagesUiTestFixture::clickable(const QString &text) const
|
||||
{
|
||||
for (QQuickItem *candidate : items()) {
|
||||
const QVariant candidateText = candidate->property("text");
|
||||
|
||||
if (candidateText.isValid() && candidateText.toString() == text && isShown(candidate)
|
||||
&& candidate->isEnabled()) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PagesUiTestFixture::click(QQuickItem *target)
|
||||
{
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPointF stablePoint;
|
||||
bool haveStablePoint = false;
|
||||
QElapsedTimer stableFor;
|
||||
|
||||
QTest::qWaitFor([&] {
|
||||
QQuickWindow *window = target->window();
|
||||
if (!window || !target->isVisible() || target->width() < 1 || target->height() < 1) {
|
||||
haveStablePoint = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QPointF point = target->mapToScene(QPointF(target->width() / 2.0, target->height() / 2.0));
|
||||
const bool inside = point.x() >= 0 && point.y() >= 0 && point.x() < window->width() && point.y() < window->height();
|
||||
const bool moved = !haveStablePoint || qAbs(point.x() - stablePoint.x()) > 1.0 || qAbs(point.y() - stablePoint.y()) > 1.0;
|
||||
|
||||
if (!inside || moved) {
|
||||
stablePoint = point;
|
||||
haveStablePoint = inside;
|
||||
stableFor.restart();
|
||||
return false;
|
||||
}
|
||||
|
||||
return stableFor.elapsed() >= 200;
|
||||
}, 5000);
|
||||
|
||||
QQuickWindow *window = target->window();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
window->requestActivate();
|
||||
QTest::qWaitForWindowExposed(window);
|
||||
|
||||
const QPointF center = target->mapToScene(QPointF(target->width() / 2.0, target->height() / 2.0));
|
||||
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, center.toPoint());
|
||||
QTest::qWait(50);
|
||||
}
|
||||
|
||||
void PagesUiTestFixture::typeText(QQuickItem *target, const QString &text)
|
||||
{
|
||||
if (!target || !m_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
click(target);
|
||||
|
||||
for (const QChar character : text) {
|
||||
QTest::keyClick(m_window, character.toLatin1());
|
||||
}
|
||||
}
|
||||
|
||||
void PagesUiTestFixture::clearAndType(QQuickItem *target, const QString &text)
|
||||
{
|
||||
if (!target || !m_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
click(target);
|
||||
|
||||
QTest::keySequence(m_window, QKeySequence::SelectAll);
|
||||
QTest::keyClick(m_window, Qt::Key_Backspace);
|
||||
|
||||
typeText(target, text);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
#include <QTest>
|
||||
|
||||
#include "secureQSettings.h"
|
||||
#include "ui/controllers/qml/pageController.h"
|
||||
#include "ui/utils/pageEnum.h"
|
||||
#include "utils/testCoreController.h"
|
||||
|
||||
class PagesUiTestFixture
|
||||
{
|
||||
public:
|
||||
bool initialize(QObject *parent);
|
||||
void cleanup();
|
||||
|
||||
bool loadPage(const QString &pageName);
|
||||
bool loadPageFromStartPage(PageLoader::PageEnum page);
|
||||
void closePage();
|
||||
|
||||
TestCoreController *core() const
|
||||
{
|
||||
return m_core;
|
||||
}
|
||||
|
||||
QQmlApplicationEngine *engine() const
|
||||
{
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
PageController *pageController() const
|
||||
{
|
||||
return m_pageController;
|
||||
}
|
||||
|
||||
QQuickWindow *window() const
|
||||
{
|
||||
return m_window;
|
||||
}
|
||||
|
||||
QQuickItem *page() const
|
||||
{
|
||||
return m_page;
|
||||
}
|
||||
|
||||
QList<QQuickItem *> items(QQuickItem *root = nullptr) const;
|
||||
QList<QQuickItem *> itemsOfType(const QString &qmlType, QQuickItem *root = nullptr) const;
|
||||
|
||||
QQuickItem *item(const QString &property, const QVariant &value, QQuickItem *root = nullptr) const;
|
||||
QQuickItem *visibleItem(const QString &property, const QVariant &value, QQuickItem *root = nullptr) const;
|
||||
QQuickItem *clickable(const QString &text) const;
|
||||
|
||||
void click(QQuickItem *target);
|
||||
void typeText(QQuickItem *target, const QString &text);
|
||||
void clearAndType(QQuickItem *target, const QString &text);
|
||||
|
||||
private:
|
||||
static void collectItems(QQuickItem *root, QList<QQuickItem *> &result);
|
||||
static bool isQmlType(const QObject *object, const QString &qmlType);
|
||||
|
||||
SecureQSettings *m_settings = nullptr;
|
||||
QQmlApplicationEngine *m_engine = nullptr;
|
||||
TestCoreController *m_core = nullptr;
|
||||
PageController *m_pageController = nullptr;
|
||||
|
||||
QQuickWindow *m_window = nullptr;
|
||||
QQuickItem *m_page = nullptr;
|
||||
};
|
||||
@@ -0,0 +1,247 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QMetaObject>
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
#include <QVariant>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include "core/controllers/selfhosted/importController.h"
|
||||
#include "core/utils/constants/configKeys.h"
|
||||
#include "core/utils/constants/protocolConstants.h"
|
||||
#include "ui/models/serversModel.h"
|
||||
|
||||
#include "pagesUiTestFixture.h"
|
||||
|
||||
using namespace amnezia;
|
||||
|
||||
class PageHomeTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
PagesUiTestFixture m_fixture;
|
||||
|
||||
QJsonObject createAwg2Config(const QString &serverName)
|
||||
{
|
||||
QJsonObject clientConfig;
|
||||
clientConfig[configKey::mtu] = protocols::awg::defaultMtu;
|
||||
clientConfig[configKey::junkPacketCount] = protocols::awg::defaultJunkPacketCount;
|
||||
clientConfig[configKey::junkPacketMinSize] = protocols::awg::defaultJunkPacketMinSize;
|
||||
clientConfig[configKey::junkPacketMaxSize] = protocols::awg::defaultJunkPacketMaxSize;
|
||||
clientConfig[configKey::specialJunk1] = protocols::awg::defaultSpecialJunk1;
|
||||
clientConfig[configKey::specialJunk2] = protocols::awg::defaultSpecialJunk2;
|
||||
clientConfig[configKey::specialJunk3] = protocols::awg::defaultSpecialJunk3;
|
||||
clientConfig[configKey::specialJunk4] = protocols::awg::defaultSpecialJunk4;
|
||||
clientConfig[configKey::specialJunk5] = protocols::awg::defaultSpecialJunk5;
|
||||
clientConfig[configKey::initPacketJunkSize] = QString::number(protocols::awg::initPacketJunkSizeMax);
|
||||
clientConfig[configKey::responsePacketJunkSize] = QString::number(protocols::awg::responsePacketJunkSizeMax);
|
||||
clientConfig[configKey::cookieReplyPacketJunkSize] = QString::number(protocols::awg::cookieReplyPacketJunkSizeMax);
|
||||
clientConfig[configKey::transportPacketJunkSize] = protocols::awg::defaultPadding;
|
||||
clientConfig[configKey::initPacketMagicHeader] = protocols::awg::defaultInitPacketMagicHeader;
|
||||
clientConfig[configKey::responsePacketMagicHeader] = protocols::awg::defaultResponsePacketMagicHeader;
|
||||
clientConfig[configKey::underloadPacketMagicHeader] = protocols::awg::defaultUnderloadPacketMagicHeader;
|
||||
clientConfig[configKey::transportPacketMagicHeader] = protocols::awg::defaultTransportPacketMagicHeader;
|
||||
clientConfig[configKey::clientPrivKey] = "test_client_private_key";
|
||||
clientConfig[configKey::clientPubKey] = "test_client_public_key";
|
||||
clientConfig[configKey::serverPubKey] = "test_server_public_key";
|
||||
clientConfig[configKey::pskKey] = "test_psk_key";
|
||||
clientConfig[configKey::clientIp] = "10.8.1.2";
|
||||
clientConfig[configKey::allowedIps] = QJsonArray::fromStringList({ "0.0.0.0/0" });
|
||||
|
||||
QJsonObject awgConfig;
|
||||
awgConfig[configKey::lastConfig] = QString(QJsonDocument(clientConfig).toJson());
|
||||
awgConfig[configKey::port] = protocols::awg::defaultPort;
|
||||
awgConfig[configKey::transportProto] = "udp";
|
||||
awgConfig[configKey::protocolVersion] = protocols::awg::awgV2;
|
||||
awgConfig[configKey::subnetAddress] = protocols::wireguard::defaultSubnetAddress;
|
||||
awgConfig[configKey::junkPacketCount] = protocols::awg::defaultJunkPacketCount;
|
||||
awgConfig[configKey::junkPacketMinSize] = protocols::awg::defaultJunkPacketMinSize;
|
||||
awgConfig[configKey::junkPacketMaxSize] = protocols::awg::defaultJunkPacketMaxSize;
|
||||
awgConfig[configKey::initPacketJunkSize] = QString::number(protocols::awg::initPacketJunkSizeMax);
|
||||
awgConfig[configKey::responsePacketJunkSize] = QString::number(protocols::awg::responsePacketJunkSizeMax);
|
||||
awgConfig[configKey::cookieReplyPacketJunkSize] = QString::number(protocols::awg::cookieReplyPacketJunkSizeMax);
|
||||
awgConfig[configKey::transportPacketJunkSize] = protocols::awg::defaultPadding;
|
||||
awgConfig[configKey::initPacketMagicHeader] = protocols::awg::defaultInitPacketMagicHeader;
|
||||
awgConfig[configKey::responsePacketMagicHeader] = protocols::awg::defaultResponsePacketMagicHeader;
|
||||
awgConfig[configKey::underloadPacketMagicHeader] = protocols::awg::defaultUnderloadPacketMagicHeader;
|
||||
awgConfig[configKey::transportPacketMagicHeader] = protocols::awg::defaultTransportPacketMagicHeader;
|
||||
awgConfig[configKey::specialJunk1] = protocols::awg::defaultSpecialJunk1;
|
||||
awgConfig[configKey::specialJunk2] = protocols::awg::defaultSpecialJunk2;
|
||||
awgConfig[configKey::specialJunk3] = protocols::awg::defaultSpecialJunk3;
|
||||
awgConfig[configKey::specialJunk4] = protocols::awg::defaultSpecialJunk4;
|
||||
awgConfig[configKey::specialJunk5] = protocols::awg::defaultSpecialJunk5;
|
||||
awgConfig[configKey::isThirdPartyConfig] = true;
|
||||
|
||||
QJsonObject container;
|
||||
container[configKey::container] = "amnezia-awg";
|
||||
container[configKey::awg] = awgConfig;
|
||||
|
||||
QJsonArray containers;
|
||||
containers.append(container);
|
||||
|
||||
QJsonObject config;
|
||||
config[configKey::containers] = containers;
|
||||
config[configKey::defaultContainer] = "amnezia-awg";
|
||||
config[configKey::description] = serverName;
|
||||
config[configKey::hostName] = "test.example.com";
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_fixture.initialize(this));
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
m_fixture.closePage();
|
||||
}
|
||||
|
||||
void cleanupTestCase()
|
||||
{
|
||||
m_fixture.cleanup();
|
||||
}
|
||||
|
||||
void pageHomeLoads()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageHome")));
|
||||
QVERIFY(m_fixture.page());
|
||||
}
|
||||
|
||||
void apiProtocolStateIsInitiallyEmpty()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageHome")));
|
||||
QCOMPARE(m_fixture.page()->property("apiAvailableProtocols").toList(), QVariantList { });
|
||||
QCOMPARE(m_fixture.page()->property("apiCurrentProtocol").toString(), QString());
|
||||
}
|
||||
|
||||
void protocolDisplayName_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("expectedName");
|
||||
|
||||
QTest::newRow("awg") << QStringLiteral("awg") << QStringLiteral("AmneziaWG");
|
||||
QTest::newRow("vless") << QStringLiteral("vless") << QStringLiteral("VLESS");
|
||||
QTest::newRow("wireguard") << QStringLiteral("wireguard") << QStringLiteral("wireguard");
|
||||
QTest::newRow("empty") << QString() << QString();
|
||||
}
|
||||
|
||||
void protocolDisplayName()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageHome")));
|
||||
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, expectedName);
|
||||
|
||||
QVariant result;
|
||||
|
||||
const bool invoked =
|
||||
QMetaObject::invokeMethod(m_fixture.page(), "protocolDisplayName", Q_RETURN_ARG(QVariant, result),
|
||||
Q_ARG(QVariant, QVariant(protocol)));
|
||||
|
||||
QVERIFY2(invoked, "Could not invoke PageHome.protocolDisplayName()");
|
||||
QCOMPARE(result.toString(), expectedName);
|
||||
}
|
||||
|
||||
void selectsServerFromExpandedList()
|
||||
{
|
||||
TestCoreController *core = m_fixture.core();
|
||||
|
||||
QVERIFY(core);
|
||||
QVERIFY(core->m_importCoreController);
|
||||
QVERIFY(core->m_serversRepository);
|
||||
QVERIFY(core->m_serversModel);
|
||||
|
||||
// Import servers
|
||||
QSignalSpy importFinishedSpy(core->m_importCoreController, &ImportController::importFinished);
|
||||
|
||||
core->m_importCoreController->importConfig(createAwg2Config(QStringLiteral("Server 1")));
|
||||
core->m_importCoreController->importConfig(createAwg2Config(QStringLiteral("Server 2")));
|
||||
|
||||
QCOMPARE(importFinishedSpy.count(), 2);
|
||||
QCOMPARE(core->m_serversRepository->serversCount(), 2);
|
||||
QCOMPARE(core->m_serversModel->rowCount(), 2);
|
||||
|
||||
// Find a server that is not currently selected
|
||||
const QString initialDefaultServerId = core->m_serversRepository->defaultServerId();
|
||||
|
||||
QVERIFY(!initialDefaultServerId.isEmpty());
|
||||
|
||||
int targetServerIndex = -1;
|
||||
QString targetServerId;
|
||||
QString targetServerName;
|
||||
|
||||
for (int row = 0; row < core->m_serversModel->rowCount(); ++row) {
|
||||
const QModelIndex index = core->m_serversModel->index(row, 0);
|
||||
const QString serverId = core->m_serversModel->data(index, ServersModel::ServerIdRole).toString();
|
||||
|
||||
if (serverId.isEmpty() || serverId == initialDefaultServerId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
targetServerIndex = row;
|
||||
targetServerId = serverId;
|
||||
targetServerName = core->m_serversModel->data(index, ServersModel::NameRole).toString();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
QVERIFY2(targetServerIndex >= 0, "Could not find a server different from the default server");
|
||||
QVERIFY(!targetServerId.isEmpty());
|
||||
QVERIFY(!targetServerName.isEmpty());
|
||||
|
||||
// Load PageHome
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageHome")));
|
||||
|
||||
// Find the collapsed HomeServerSummary
|
||||
const QList<QQuickItem *> summaries = m_fixture.itemsOfType(QStringLiteral("HomeServerSummary"));
|
||||
|
||||
QVERIFY(!summaries.isEmpty());
|
||||
|
||||
QQuickItem *collapsedSummary = nullptr;
|
||||
|
||||
for (QQuickItem *summary : summaries) {
|
||||
const QVariant expanded = summary->property("expanded");
|
||||
|
||||
if (expanded.isValid() && !expanded.toBool()) {
|
||||
collapsedSummary = summary;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY2(collapsedSummary, "Collapsed HomeServerSummary was not found");
|
||||
|
||||
// Open the server drawer
|
||||
QVERIFY(QMetaObject::invokeMethod(collapsedSummary, "expandRequested"));
|
||||
|
||||
QQuickItem *targetServerItem = nullptr;
|
||||
|
||||
QTRY_VERIFY((targetServerItem = m_fixture.visibleItem(QStringLiteral("text"), targetServerName)) != nullptr);
|
||||
QVERIFY(targetServerItem);
|
||||
QVERIFY(targetServerItem->isEnabled());
|
||||
|
||||
// Select the target server through the UI
|
||||
m_fixture.click(targetServerItem);
|
||||
|
||||
// Verify that the selected server has changed
|
||||
QTRY_COMPARE(core->m_serversRepository->defaultServerId(), targetServerId);
|
||||
|
||||
// Verify that the selection is reflected in ServersModel
|
||||
const QModelIndex selectedServerIndex = core->m_serversModel->index(targetServerIndex, 0);
|
||||
|
||||
QVERIFY(selectedServerIndex.isValid());
|
||||
QCOMPARE(core->m_serversModel->data(selectedServerIndex, ServersModel::IsDefaultRole).toBool(), true);
|
||||
}
|
||||
};
|
||||
|
||||
int runPageHomeTests(int argc, char *argv[])
|
||||
{
|
||||
PageHomeTest test;
|
||||
return QTest::qExec(&test, argc, argv);
|
||||
}
|
||||
|
||||
#include "testPageHome.moc"
|
||||
@@ -0,0 +1,203 @@
|
||||
#include <QQmlProperty>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
#include <QUrl>
|
||||
|
||||
#include "ui/utils/pageEnum.h"
|
||||
|
||||
#include "pagesUiTestFixture.h"
|
||||
|
||||
class TestPageSetupWizardConfigSource : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
PagesUiTestFixture m_fixture;
|
||||
|
||||
QQuickItem *findCard(const QString &text) const
|
||||
{
|
||||
return m_fixture.item("text", text);
|
||||
}
|
||||
|
||||
QQuickItem *findMoreButton() const
|
||||
{
|
||||
const QList<QQuickItem *> buttons = m_fixture.itemsOfType(QStringLiteral("IconButtonType"));
|
||||
|
||||
for (QQuickItem *button : buttons) {
|
||||
if (!button->isVisible() || !button->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QUrl source = QQmlProperty(button, QStringLiteral("icon.source")).read().toUrl();
|
||||
|
||||
if (source.toString().contains(QStringLiteral("more-vertical"))) {
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_fixture.initialize(this));
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
m_fixture.closePage();
|
||||
}
|
||||
|
||||
void cleanupTestCase()
|
||||
{
|
||||
m_fixture.cleanup();
|
||||
}
|
||||
|
||||
void pageLoads()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageSetupWizardConfigSource")));
|
||||
QVERIFY(m_fixture.page());
|
||||
}
|
||||
|
||||
void connectionOptionsAreVisible()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageSetupWizardConfigSource")));
|
||||
|
||||
QQuickItem *amneziaVpnCard = findCard(QStringLiteral("VPN by Amnezia"));
|
||||
QQuickItem *selfHostedVpnCard = findCard(QStringLiteral("Self-hosted VPN"));
|
||||
|
||||
QVERIFY2(amneziaVpnCard, "VPN by Amnezia card was not found");
|
||||
QVERIFY2(selfHostedVpnCard, "Self-hosted VPN card was not found");
|
||||
|
||||
QVERIFY(amneziaVpnCard->isVisible());
|
||||
QVERIFY(selfHostedVpnCard->isVisible());
|
||||
|
||||
QVERIFY(amneziaVpnCard->isEnabled());
|
||||
QVERIFY(selfHostedVpnCard->isEnabled());
|
||||
}
|
||||
|
||||
void restoringBackupDisablesConnectionOptions()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageSetupWizardConfigSource")));
|
||||
|
||||
QQuickItem *amneziaVpnCard = findCard(QStringLiteral("VPN by Amnezia"));
|
||||
QQuickItem *selfHostedVpnCard = findCard(QStringLiteral("Self-hosted VPN"));
|
||||
|
||||
QVERIFY(amneziaVpnCard);
|
||||
QVERIFY(selfHostedVpnCard);
|
||||
|
||||
QVERIFY(m_fixture.page()->setProperty("isRestoringBackup", true));
|
||||
|
||||
QVERIFY(!amneziaVpnCard->isEnabled());
|
||||
QVERIFY(!selfHostedVpnCard->isEnabled());
|
||||
}
|
||||
|
||||
void finishingBackupRestoreEnablesConnectionOptions()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageSetupWizardConfigSource")));
|
||||
|
||||
QQuickItem *amneziaVpnCard = findCard(QStringLiteral("VPN by Amnezia"));
|
||||
QQuickItem *selfHostedVpnCard = findCard(QStringLiteral("Self-hosted VPN"));
|
||||
|
||||
QVERIFY(amneziaVpnCard);
|
||||
QVERIFY(selfHostedVpnCard);
|
||||
|
||||
QVERIFY(m_fixture.page()->setProperty("isRestoringBackup", true));
|
||||
|
||||
QVERIFY(!amneziaVpnCard->isEnabled());
|
||||
QVERIFY(!selfHostedVpnCard->isEnabled());
|
||||
|
||||
QVERIFY(m_fixture.page()->setProperty("isRestoringBackup", false));
|
||||
|
||||
QVERIFY(amneziaVpnCard->isEnabled());
|
||||
QVERIFY(selfHostedVpnCard->isEnabled());
|
||||
}
|
||||
|
||||
void selectingSelfHostedVpnOpensCredentialsPage()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPage(QStringLiteral("PageSetupWizardConfigSource")));
|
||||
|
||||
QQuickItem *selfHostedVpnCard = findCard(QStringLiteral("Self-hosted VPN"));
|
||||
|
||||
QVERIFY(selfHostedVpnCard);
|
||||
QVERIFY(selfHostedVpnCard->isVisible());
|
||||
QVERIFY(selfHostedVpnCard->isEnabled());
|
||||
|
||||
QSignalSpy navigationSpy(m_fixture.pageController(), &PageController::goToPage);
|
||||
|
||||
QVERIFY(navigationSpy.isValid());
|
||||
|
||||
m_fixture.click(selfHostedVpnCard);
|
||||
|
||||
QTRY_COMPARE(navigationSpy.count(), 1);
|
||||
|
||||
const QList<QVariant> arguments = navigationSpy.takeFirst();
|
||||
|
||||
QCOMPARE(arguments.at(0).toInt(), static_cast<int>(PageLoader::PageEnum::PageSetupWizardCredentials));
|
||||
}
|
||||
|
||||
void moreMenuCanBeOpened()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPageFromStartPage(PageLoader::PageEnum::PageSetupWizardConfigSource));
|
||||
QVERIFY(m_fixture.pageController()->isStartPageVisible());
|
||||
|
||||
QQuickItem *moreButton = findMoreButton();
|
||||
|
||||
QVERIFY2(moreButton, "Visible More button was not found");
|
||||
|
||||
m_fixture.click(moreButton);
|
||||
|
||||
QTRY_VERIFY(m_fixture.visibleItem(QStringLiteral("text"), QStringLiteral("Settings")) != nullptr);
|
||||
QTRY_VERIFY(m_fixture.visibleItem(QStringLiteral("text"), QStringLiteral("Enable logs")) != nullptr);
|
||||
}
|
||||
|
||||
void loggingSwitchCanBeToggledFromMoreMenu()
|
||||
{
|
||||
QVERIFY(m_fixture.loadPageFromStartPage(PageLoader::PageEnum::PageSetupWizardConfigSource));
|
||||
QVERIFY(m_fixture.pageController()->isStartPageVisible());
|
||||
|
||||
QQuickItem *moreButton = findMoreButton();
|
||||
|
||||
QVERIFY2(moreButton, "Visible More button was not found");
|
||||
|
||||
m_fixture.click(moreButton);
|
||||
|
||||
QQuickItem *loggingRow = nullptr;
|
||||
|
||||
QTRY_VERIFY((loggingRow = m_fixture.visibleItem(QStringLiteral("text"), QStringLiteral("Enable logs"))) != nullptr);
|
||||
|
||||
const QList<QQuickItem *> switches = m_fixture.itemsOfType(QStringLiteral("SwitchType"));
|
||||
|
||||
QVERIFY2(!switches.isEmpty(), "Logging switch was not found");
|
||||
|
||||
QQuickItem *loggingSwitch = nullptr;
|
||||
|
||||
for (QQuickItem *candidate : switches) {
|
||||
if (candidate->isVisible() && candidate->property("checked").isValid()) {
|
||||
loggingSwitch = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY2(loggingSwitch, "Visible logging switch was not found");
|
||||
|
||||
const bool initialState = loggingSwitch->property("checked").toBool();
|
||||
|
||||
m_fixture.click(loggingRow);
|
||||
|
||||
QTRY_VERIFY(loggingSwitch->property("checked").toBool() != initialState);
|
||||
|
||||
m_fixture.click(loggingRow);
|
||||
|
||||
QTRY_COMPARE(loggingSwitch->property("checked").toBool(), initialState);
|
||||
}
|
||||
};
|
||||
|
||||
int runPageSetupWizardConfigSourceTests(int argc, char *argv[])
|
||||
{
|
||||
TestPageSetupWizardConfigSource test;
|
||||
return QTest::qExec(&test, argc, argv);
|
||||
}
|
||||
|
||||
#include "testPageSetupWizardConfigSource.moc"
|
||||
Reference in new issue
Block a user