From 13988da4fc2346f2ef47a4d8bd2483b7f2b0d3ae Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Thu, 20 Jul 2023 07:14:23 +0000 Subject: [PATCH] Add login appium test --- .kde-ci.yml | 4 +++ CMakeLists.txt | 7 +++-- appiumtests/CMakeLists.txt | 23 +++++++++++++++ appiumtests/login-server.py | 42 ++++++++++++++++++++++++++ appiumtests/logintest.py | 44 ++++++++++++++++++++++++++++ src/main.cpp | 8 +++++ src/qml/Component/Login/Login.qml | 1 + src/qml/Component/Login/Password.qml | 1 + 8 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 appiumtests/CMakeLists.txt create mode 100644 appiumtests/login-server.py create mode 100755 appiumtests/logintest.py diff --git a/.kde-ci.yml b/.kde-ci.yml index 240f77b3c..04dc06df2 100644 --- a/.kde-ci.yml +++ b/.kde-ci.yml @@ -58,5 +58,9 @@ Dependencies: 'require': 'frameworks/kdbusaddons': '@latest-kf6' +- 'on': ['Linux/Qt6', 'Linux/Qt5'] + 'require': + 'sdk/selenium-webdriver-at-spi': '@latest-kf6' + Options: require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD', 'Windows' ] diff --git a/CMakeLists.txt b/CMakeLists.txt index 45ed228b1..f6a77977d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,8 +176,11 @@ add_definitions(-DQT_NO_FOREACH) add_subdirectory(src) -find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test) -add_subdirectory(autotests) +if (BUILD_TESTING) + find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test) + add_subdirectory(autotests) + add_subdirectory(appiumtests) +endif() if(KF${QT_MAJOR_VERSION}DocTools_FOUND) kdoctools_install(po) diff --git a/appiumtests/CMakeLists.txt b/appiumtests/CMakeLists.txt new file mode 100644 index 000000000..a60fbe5c8 --- /dev/null +++ b/appiumtests/CMakeLists.txt @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: BSD-3-Clause +# SPDX-FileCopyrightText: 2022 Harald Sitter + + +if(NOT BUILD_TESTING OR NOT CMAKE_SYSTEM_NAME MATCHES "Linux") + return() +endif() + +find_package(SeleniumWebDriverATSPI) +set_package_properties(SeleniumWebDriverATSPI PROPERTIES + DESCRIPTION "Server component for selenium tests using Linux accessibility infrastructure" + PURPOSE "Needed for GUI tests" + URL "https://invent.kde.org/sdk/selenium-webdriver-at-spi" + TYPE OPTIONAL +) +if(NOT SeleniumWebDriverATSPI_FOUND) + return() +endif() + +add_test( + NAME logintest + COMMAND selenium-webdriver-at-spi-run ${CMAKE_CURRENT_SOURCE_DIR}/logintest.py +) diff --git a/appiumtests/login-server.py b/appiumtests/login-server.py new file mode 100644 index 000000000..f8c0a91de --- /dev/null +++ b/appiumtests/login-server.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2023 Tobias Fella + + +from flask import Flask, request, abort +app = Flask(__name__) + +@app.route("/_matrix/client/v3/login", methods=["GET"]) +def login_get(): + result = dict() + result["flows"] = [dict()] + result["flows"][0]["type"] = "m.login.password" + return result + +@app.route("/_matrix/client/v3/login", methods=["POST"]) +def login_post(): + data = request.get_json() + if data["identifier"]["user"] != "user" or data["password"] != "1234": + abort(403) + print(data) + result = dict() + result["access_token"] = "token_1234" + result["device_id"] = "device_1234" + result["user_id"] = "@user:localhost:1234" + return result + +@app.route("/_matrix/client/r0/sync") +def sync(): + result = dict() + result["next_batch"] = "batch1234" + return result + +@app.route("/.well-known/matrix/client") +def well_known(): + reply = dict() + reply["m.homeserver"] = dict() + reply["m.homeserver"]["base_url"] = "https://localhost:1234" + return reply + + +if __name__ == "__main__": + app.run(ssl_context='adhoc', port=1234) diff --git a/appiumtests/logintest.py b/appiumtests/logintest.py new file mode 100755 index 000000000..39a6303da --- /dev/null +++ b/appiumtests/logintest.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2021-2022 Harald Sitter +# SPDX-FileCopyrightText: 2023 Tobias Fella + +import unittest +from appium import webdriver +from appium.webdriver.common.appiumby import AppiumBy +from selenium.webdriver.support.ui import WebDriverWait +import time +import subprocess + +class LoginTest(unittest.TestCase): + @classmethod + def setUpClass(self): + desired_caps = {} + desired_caps["app"] = "neochat --ignore-ssl-errors" + desired_caps["timeouts"] = {'implicit': 10000} + self.driver = webdriver.Remote( + command_executor='http://127.0.0.1:4723', + desired_capabilities=desired_caps) + subprocess.Popen(["python","login-server.py"]) + + def setUp(self): + pass + + def tearDown(self): + if not self._outcome.result.wasSuccessful(): + self.driver.get_screenshot_as_file("failed_test_shot_{}.png".format(self.id())) + + @classmethod + def tearDownClass(self): + self.driver.quit() + + def test_login(self): + self.driver.find_element(by=AppiumBy.NAME, value="Matrix ID").send_keys("@user:localhost:1234") + self.driver.find_element(by=AppiumBy.NAME, value="Continue").click() + self.driver.find_element(by=AppiumBy.NAME, value="Password").send_keys("1234") + self.driver.find_element(by=AppiumBy.NAME, value="Login").click() + self.driver.find_element(by=AppiumBy.NAME, value="Join some rooms to get started").click() + +if __name__ == '__main__': + unittest.main() diff --git a/src/main.cpp b/src/main.cpp index 6a2f8cf81..4b80e8841 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -323,11 +324,18 @@ int main(int argc, char *argv[]) QCommandLineParser parser; parser.setApplicationDescription(i18n("Client for the matrix communication protocol")); parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports matrix: url scheme")); + parser.addOption(QCommandLineOption("ignore-ssl-errors", i18n("Ignore all SSL Errors, e.g., unsigned certificates."))); about.setupCommandLine(&parser); parser.process(app); about.processCommandLine(&parser); + if (parser.isSet("ignore-ssl-errors")) { + QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) { + reply->ignoreSslErrors(); + }); + } + engine.addImageProvider(QLatin1String("mxc"), new MatrixImageProvider); engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider); diff --git a/src/qml/Component/Login/Login.qml b/src/qml/Component/Login/Login.qml index 347c798be..93f155d57 100644 --- a/src/qml/Component/Login/Login.qml +++ b/src/qml/Component/Login/Login.qml @@ -28,6 +28,7 @@ LoginStep { id: matrixIdField Kirigami.FormData.label: i18n("Matrix ID:") placeholderText: "@user:matrix.org" + Accessible.name: i18n("Matrix ID") onTextChanged: { LoginHelper.matrixId = text } diff --git a/src/qml/Component/Login/Password.qml b/src/qml/Component/Login/Password.qml index b7ef00811..c42ba1acc 100644 --- a/src/qml/Component/Login/Password.qml +++ b/src/qml/Component/Login/Password.qml @@ -38,6 +38,7 @@ LoginStep { id: passwordField onTextChanged: LoginHelper.password = text enabled: !LoginHelper.isLoggingIn + Accessible.name: i18n("Password") Component.onCompleted: { passwordField.forceActiveFocus()