Compare commits

...
17 Commits
7 changed files with 939 additions and 0 deletions

No files matched your search

+3
View File
@@ -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
+30
View File
@@ -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).
+35
View File
@@ -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;
}
+350
View File
@@ -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);
}
+71
View File
@@ -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;
};
+247
View File
@@ -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"