Page MenuHomePhorge

No OneTemporary

Size
37 KB
Referenced Files
None
Subscribers
None
diff --git a/.gitignore b/.gitignore
index d94b87f..9250b02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,16 @@
*~
\#*
.#*
build
node_modules
TAGS
.flatpak-builder
cargo-sources.json
packaging/GNU-Linux/flatpak/*.flatpak
packaging/GNU-Linux/flatpak/repo
packaging/GNU-Linux/flatpak/venv
packaging/GNU-Linux/flatpak/flatpak-build
packaging/GNU-Linux/flatpak/vodozemac-bindings
+*.pyc
+*.pyo
+__pycache__
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2dca4d0..6a7d09f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,94 +1,105 @@
cmake_minimum_required(VERSION 3.16)
project(kazv)
include(FeatureSummary)
set(kazv_VERSION_MAJOR 0)
set(kazv_VERSION_MINOR 6)
set(kazv_VERSION_PATCH 0)
set(kazv_VERSION_STRING ${kazv_VERSION_MAJOR}.${kazv_VERSION_MINOR}.${kazv_VERSION_PATCH})
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(CMAKE_CXX_STANDARD 20)
if(NOT kazv_KF_QT_MAJOR_VERSION)
set(kazv_KF_QT_MAJOR_VERSION 6)
endif()
message(STATUS "KF/Qt major version: ${kazv_KF_QT_MAJOR_VERSION}")
if(NOT kazv_KF_QT_MAJOR_VERSION EQUAL 6)
message(FATAL_ERROR "KF/Qt major version must be 6")
endif()
set(QT_MAJOR_VERSION ${kazv_KF_QT_MAJOR_VERSION})
set(KF_MAJOR_VERSION ${kazv_KF_QT_MAJOR_VERSION})
set(QT_MIN_VERSION 6.5.0)
set(KF_MIN_VERSION 6.0.0)
option(kazv_LINK_BREEZE_ICONS "Link to Breeze icons library" OFF)
+option(kazv_ENABLE_APPIUM_TESTS "Run appium tests" "")
set(kazv_LINK_BREEZE_ICONS_MACRO 0)
if(kazv_LINK_BREEZE_ICONS)
set(kazv_LINK_BREEZE_ICONS_MACRO 1)
endif()
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
set(KDE_SKIP_NULLPTR_WARNINGS_SETTINGS ON)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMSetupVersion)
include(ECMGenerateHeaders)
include(ECMPoQmTools)
include(ECMAddTests)
include(ECMQtDeclareLoggingCategory)
include(ECMDeprecationSettings)
include(ECMQmlModule)
ecm_set_disabled_deprecation_versions(
QT 5.15.14
KF 5.116.0
KCOREADDONS 5.116.0
)
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS
Core Gui Qml QuickControls2 Svg Concurrent Widgets
Multimedia Test Network QuickTest HttpServer Sql
)
qt6_policy(SET QTP0001 NEW)
find_package(QCoro6 REQUIRED COMPONENTS Core)
qcoro_enable_coroutines()
set(kazv_KF_EXTRA_MODULES)
if(kazv_LINK_BREEZE_ICONS)
list(APPEND kazv_KF_EXTRA_MODULES BreezeIcons)
endif()
find_package(KF${KF_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami Config KIO Notifications CoreAddons ${kazv_KF_EXTRA_MODULES})
find_package(KF6KirigamiAddons)
find_package(Threads REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(libkazv REQUIRED)
find_package(cmark)
set(CMARK_TARGET_NAME cmark::cmark)
if(NOT cmark_FOUND)
# CMark 0.29.0 on Ubuntu 20.04 has cmark.cmake instead of cmark-config.cmake
# But that is not usable, so we fall back to pkgconfig
find_package(PkgConfig REQUIRED)
pkg_search_module(cmark libcmark IMPORTED_TARGET GLOBAL)
set(CMARK_TARGET_NAME cmark)
endif()
+if(${kazv_ENABLE_APPIUM_TESTS} STREQUAL "")
+ find_package(SeleniumWebDriverATSPI)
+ if(SeleniumWebDriverATSPI_FOUND)
+ message(STATUS "Enabling appium tests because selenium-webdriver-at-spi is found. To turn it off, explicitly set kazv_ENABLE_APPIUM_TESTS to OFF")
+ set(kazv_ENABLE_APPIUM_TESTS ON)
+ endif()
+elseif(kazv_ENABLE_APPIUM_TESTS)
+ find_package(SeleniumWebDriverATSPI REQUIRED)
+endif()
+
kde_enable_exceptions()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -foperator-names -DQT_NO_EMIT")
add_subdirectory(src)
install(PROGRAMS moe.kazv.mxc.kazv.desktop DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES moe.kazv.mxc.kazv.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
install(FILES kazv.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
install(FILES icons/kazvlogo-256.png DESTINATION ${KDE_INSTALL_ICONDIR}/hicolor/256x256/apps RENAME moe.kazv.mxc.kazv.png)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/README.md b/README.md
index 0c87e18..d3507bb 100644
--- a/README.md
+++ b/README.md
@@ -1,127 +1,131 @@
# Description
kazv is a convergent matrix client and instant messaging app.
# License
Copyright (C) 2020-2023 the Kazv Project <https://lily-is.land/kazv>
kazv is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
kazv is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with kazv. If not, see <https://www.gnu.org/licenses/>.
# Compiled executables
| Operating system | Architecture | Format | Download links |
|------------------|--------------|----------|----------------------------------------------------|
| GNU/Linux | amd64 | AppImage | [Debug][gl-amd64-ai-d] [Production][gl-amd64-ai-p] |
[gl-amd64-ai-d]: https://lily.kazv.moe/kazv/kazv/-/jobs/artifacts/servant/download?job=build:GNU/Linux-amd64-debug:kf6
[gl-amd64-ai-p]: https://lily.kazv.moe/kazv/kazv/-/jobs/artifacts/servant/download?job=build:GNU/Linux-amd64-prod:kf6
# Installation
## GNU/ or musl/Linux
### flatpak
If you have flatpak, you can install kazv via flatpak. Because flatpak currently cannot be built in an unprivileged container, we are unfortunately unable to provide a built image as a part of CI/CD. See [this flatpak bug][flatpak-docker-bug] for more details. It seems that upstream is very uninterested in resolving this security issue, as this bug report has been open for more than five years with zero (0) comment from Collaborators of this repository.
[flatpak-docker-bug]: https://github.com/flatpak/flatpak/issues/3027
However, we will release *manually-built* flatpak packages from version 0.6.0 onwards. You can also build the flatpak via the instructions specified in the README file in the `packaging/GNU-Linux/flatpak` directory.
### Gentoo
If you are using Gentoo, you can use [tusooa-overlay][tusooa-overlay]
to install kazv.
[tusooa-overlay]: https://gitlab.com/tusooa/tusooa-overlay
### Nix/NixOS
Stable kazv is available as a [part of nixpkgs][nixpkgs].
Development version is available in [XYenon's nur-packages repository][xyenon-repo].
[nixpkgs]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/ka/kazv/package.nix
[xyenon-repo]: https://github.com/XYenon/nur-packages
## Windows
You can build kazv using [Craft][craft]. You will need to use [our Craft blueprints][craft-blueprints-project-kazv]. Follow the instructions in that repository to build and run kazv. We also upload the binaries to the [release page][kazv-release].
[craft]: https://community.kde.org/Craft
[craft-blueprints-project-kazv]: https://lily-is.land/kazv/craft-blueprints-project-kazv
[kazv-release]: https://lily-is.land/kazv/kazv/-/releases
## Dependencies
- ECM
- Qt6: Core Gui Qml QuickControls2 Svg Concurrent Multimedia Network Widgets (ImageFormats is a recommended runtime dependency, without which formats like webp cannot be displayed) (Test and QuickTest if you want to build tests)
- KF6: Kirigami KConfig KIO Notifications CoreAddons KirigamiAddons (BreezeIcons is recommended)
- nlohmann_json
- libkazv
- cmark
## Build process
Normal build:
```
mkdir build && cd build
cmake ..
make install
```
Updating external JavaScript libraries (advanced, usually not needed):
You need to first have Node.js available.
```
cd src/js/Intl.js
npm install
cd ..
npm install
./transform.bash
```
Follow the instruction in [emoji.lisp](./tools/emoji.lisp) to update the emoji list (usually not needed)
+## Appium tests
+
+Some of the tests use appium. Please refer to the [article on KDE develop site](https://develop.kde.org/docs/apps/tests/appium/) for guides on how to setup appium and selenium-webdriver-at-spi. Once you have them installed, you can pass `-Dkazv_ENABLE_APPIUM_TESTS=ON` to enable them. The tests will run together with other tests when you invoke `ctest` or `make test`.
+
# Contributing
## Dev chat
We use Matrix to communicate. Join the matrix room at [#kazv:tusooa.xyz](https://matrix.to/#/#kazv:tusooa.xyz).
## Bugs
Report bugs to [our repository on Lily Islands](https://lily-is.land/kazv/kazv) (chances are it is the page you are currently viewing), or email to [project-bugs@kazv.moe](mailto:project-bugs@kazv.moe).
Report security vulnerabilities using a confidential GitLab issue on Lily Islands, or email to [project-security@kazv.moe](mailto:project-security@kazv.moe).
## Write code
To contribute code to the Kazv Project, open a merge request on Lily Islands, or email your patch to [project-patch@kazv.moe](mailto:project-patch@kazv.moe).
## Translations
kazv uses [fluent](https://projectfluent.org) for translations. To add a new language, add it into `src/l10n/translations.qrc`, `src/l10n/config.json`, and populate translations in the files under `src/l10n/<language code>`.
Before a change can be landed, the UI has to be translated into all of the **core languages**. One who submits a patch only needs to do the translations for at **least one** core language. The rest can be translated by others.
The list of **core languages** is defined in `src/js/l10n-config.json`. If you want to add other languages into the list and you have enough commitment to keep the translations up-to-date (refer to the rule before), you can submit a patch to do so.
We have a CI job to validate all the translation files, and ensure translations in all of the core languages are up-to-date.
## Donate
You can now donate to the Kazv Project on Liberapay: <https://liberapay.com/theKazvProject/>.
diff --git a/packaging/GNU-Linux/appimage/build-unprivileged.sh b/packaging/GNU-Linux/appimage/build-unprivileged.sh
index 530f835..a2078bd 100755
--- a/packaging/GNU-Linux/appimage/build-unprivileged.sh
+++ b/packaging/GNU-Linux/appimage/build-unprivileged.sh
@@ -1,10 +1,20 @@
#!/bin/bash
export XDG_RUNTIME_DIR=/run/user/test
+git clone https://invent.kde.org/sdk/selenium-webdriver-at-spi.git
+pushd selenium-webdriver-at-spi
+virtualenv --system-site-packages venv
+source ./venv/bin/activate
+pip3 install -r requirements.txt
+CC=/usr/lib/ccache/gcc CXX=/usr/lib/ccache/g++ cmake -B build/
+cmake --build build/
+cmake --install build/
+popd
+
CC=/usr/lib/ccache/gcc CXX=/usr/lib/ccache/g++ \
cmake .. -DCMAKE_INSTALL_PREFIX="$KAZV_INSTALL_DIR" \
- -DCMAKE_PREFIX_PATH="$LIBKAZV_INSTALL_DIR;$DEPS_INSTALL_DIR" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Dkazv_KF_QT_MAJOR_VERSION=$KF_VER -Dkazv_LINK_BREEZE_ICONS=ON && \
+ -DCMAKE_PREFIX_PATH="$LIBKAZV_INSTALL_DIR;$DEPS_INSTALL_DIR" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -Dkazv_KF_QT_MAJOR_VERSION=$KF_VER -Dkazv_LINK_BREEZE_ICONS=ON -Dkazv_ENABLE_APPIUM_TESTS=ON && \
make -j$JOBS && \
make -j$JOBS DESTDIR=AppDir install && \
dbus-launch --exit-with-session -- bash "$thisDir"/test.sh
diff --git a/packaging/GNU-Linux/appimage/build.sh b/packaging/GNU-Linux/appimage/build.sh
index 14fc338..c0c1162 100755
--- a/packaging/GNU-Linux/appimage/build.sh
+++ b/packaging/GNU-Linux/appimage/build.sh
@@ -1,118 +1,141 @@
#!/bin/bash
set -x
set -e
export thisDir="$(readlink -f "$(dirname "$(realpath "$0")")")"
# Collect deps
wget https://raw.githubusercontent.com/KDE/docker-neon/Neon/stable/public.key && \
apt-key add public.key && \
rm public.key
DEP_PACKAGES=(
extra-cmake-modules
wget
weston
dbus-x11
libcmark-dev
)
if [[ $KF_VER == 5 ]]; then
echo "Building on KF5 is no longer supported."
exit 1
else
DEP_PACKAGES+=(
qt6-base-dev
qt6-declarative-dev
qt6-multimedia-dev
qt6-imageformats
qml6-module-qtmultimedia
libqt6concurrent6
libqt6gui6
libqt6svg6-dev
libqt6quickcontrols2-6
qml6-module-qtquick-controls
qml6-module-qtquick-layouts
qml6-module-qt-labs-qmlmodels
kf6-kirigami2-dev
libkf6config-dev
qt6-gtk-platformtheme
plasma-integration
libkf6kio-dev
qml6-module-qttest
qml6-module-qt-labs-platform
libkf6notifications-dev
qml6-module-qtquick-dialogs
qml6-module-org-kde-notification
kf6-kirigami-addons-dev
kf6-kcoreaddons-dev
qml6-module-org-kde-qqc2desktopstyle
kf6-qqc2-desktop-style-dev
kf6-breeze-icon-theme-dev
cmark
qt6-httpserver-dev
qt6-websockets-dev
libqt6sql6-sqlite
libqcoro6core0t64
qcoro-qt6-dev
+ # https://develop.kde.org/docs/apps/tests/appium/#building-manually
+ libkf6windowsystem-dev
+ libwayland-dev
+ libkpipewire-dev
+ kwin-dev
+ kwayland-dev
+ plasma-wayland-protocols
+ python3-pip
+ python3-virtualenv
+ ruby
+ accerciser
+ orca
)
export QMAKE=qmake6
cp -v packaging/GNU-Linux/appimage/kde-neon-noble.list /etc/apt/sources.list.d/
fi
apt-get update && \
apt-get -y upgrade && \
DEBIAN_FRONTEND="noninteractive" \
apt-get -y install aptitude && \
aptitude -y install "${DEP_PACKAGES[@]}"
export BUILD_TYPE="$1"
if [[ ! "$BUILD_TYPE" ]]; then
export BUILD_TYPE=Debug
fi
+git clone https://invent.kde.org/sdk/selenium-webdriver-at-spi.git
+pushd selenium-webdriver-at-spi
+virtualenv --system-site-packages venv
+source ./venv/bin/activate
+pip3 install -r requirements.txt
+CC=/usr/lib/ccache/gcc CXX=/usr/lib/ccache/g++ cmake -B build/
+cmake --build build/
+cmake --install build/
+popd
+
useradd -m -U builder
mkdir build
+mkdir -pv ccache
chown -R builder:builder ccache
chown builder:builder build
cd build
export JOBS=${JOBS:-$(( $(nproc) / 2))} DEPS_INSTALL_DIR=/opt/libkazv-deps LIBKAZV_INSTALL_DIR=/opt/libkazv KAZV_INSTALL_DIR=/usr
export LD_LIBRARY_PATH="$LIBKAZV_INSTALL_DIR/lib:$LIBKAZV_INSTALL_DIR/lib/x86_64-linux-gnu:$DEPS_INSTALL_DIR/lib:$DEPS_INSTALL_DIR/lib/x86_64-linux-gnu"
mkdir -pv /run/user/test
chown builder:builder /run/user/test
chmod 700 /run/user/test
su builder -c "env PATH='$PATH' '$thisDir'/build-unprivileged.sh" || exit 1
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
chmod +x linuxdeploy*.AppImage
export QML_SOURCES_PATHS=../src
./linuxdeploy-plugin-qt-x86_64.AppImage --appimage-extract
./linuxdeploy-x86_64.AppImage --appimage-extract
# https://github.com/linuxdeploy/linuxdeploy/issues/154
mv -v ./linuxdeploy-plugin-qt-x86_64.AppImage ./linuxdeploy-notplugin-qt-x86_64.AppImage
# Enable the appimage to use Plasma themes, linuxdeploy-plugin-qt will only copy the gtk3 plugin -((
mkdir -pv AppDir/usr/plugins/platformthemes/
cp /usr/lib/x86_64-linux-gnu/qt6/plugins/platformthemes/KDEPlasmaPlatformTheme6.so AppDir/usr/plugins/platformthemes/ || true
mkdir -pv AppDir/usr/qml/org/kde
cp -rv /usr/lib/x86_64-linux-gnu/qt6/qml/org/kde/desktop AppDir/usr/qml/org/kde
cp -rv /usr/lib/x86_64-linux-gnu/qt6/qml/org/kde/qqc2desktopstyle AppDir/usr/qml/org/kde
mkdir -pv AppDir/usr/plugins/kf6/kirigami/platform
cp -v /usr/lib/x86_64-linux-gnu/qt6/plugins/kf6/kirigami/platform/org.kde.desktop.so AppDir/usr/plugins/kf6/kirigami/platform/
# Workaround "cannot mix incompatible qt versions" problem
mkdir -pv AppDir/apprun-hooks/
cp -v "$thisDir"/hooks/*.sh AppDir/apprun-hooks/
./squashfs-root/AppRun --appdir AppDir -i ../icons/kazvlogo-256.png --plugin qt --output appimage # -l /lib/x86_64-linux-gnu/libstdc++.so.6
cp kazv*.AppImage ../kazv-"$BUILD_TYPE".AppImage
diff --git a/packaging/GNU-Linux/appimage/test.sh b/packaging/GNU-Linux/appimage/test.sh
index 4acf44c..4d3de9d 100644
--- a/packaging/GNU-Linux/appimage/test.sh
+++ b/packaging/GNU-Linux/appimage/test.sh
@@ -1,23 +1,23 @@
#!/bin/bash
# inspired by https://github.com/johanhelsing/docker-qt-tests/blob/master/scripts/test-qt.sh
set -x
echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
weston -S wayland-test --backend=headless-backend.so --idle-time=0 &
sleep 5
ls /run/user/test
socket=/run/user/test/wayland-test
echo "Wayland socket is $socket"
-CTEST_OUTPUT_ON_FAILURE=ON QT_QPA_PLATFORM=wayland WAYLAND_DISPLAY="$socket" make test
+TEST_WITH_KWIN_WAYLAND=0 CTEST_OUTPUT_ON_FAILURE=ON QT_QPA_PLATFORM=wayland WAYLAND_DISPLAY="$socket" make test
ret=$?
echo "Tests returned $ret"
kill %1
exit $ret
diff --git a/src/contents/ui/StickerPicker.qml b/src/contents/ui/StickerPicker.qml
index d5fea29..57131dd 100644
--- a/src/contents/ui/StickerPicker.qml
+++ b/src/contents/ui/StickerPicker.qml
@@ -1,91 +1,104 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.13 as Kirigami
import moe.kazv.mxc.kazv 0.0 as MK
import '.' as Kazv
ColumnLayout {
id: stickerPicker
property var stickerPackList
spacing: 0
property var currentIndex: 0
signal sendMessageRequested(var eventJson)
TabBar {
id: packListView
Layout.fillWidth: true
Layout.minimumHeight: Kirigami.Units.gridUnit * 2
Repeater {
model: stickerPackList
delegate: TabButton {
id: packDelegate
objectName: `stickerPack${index}`
property var pack: stickerPackList.at(index)
property var packNameProvider: Kazv.StickerPackNameProvider {
pack: packDelegate.pack
}
icon.name: 'smiley'
text: packNameProvider.name
checked: stickerPicker.currentIndex === index
- onClicked: stickerPicker.currentIndex = index
+ onClicked: setCurrent()
+ Accessible.onPressAction: setCurrent()
+ function setCurrent() {
+ stickerPicker.currentIndex = index;
+ }
}
}
}
property var currentPack: stickerPackList.at(currentIndex)
property var stickerSize: Kirigami.Units.iconSizes.enormous
property var stickerMargin: Kirigami.Units.smallSpacing
GridView {
id: stickersGridView
Layout.fillWidth: true
Layout.minimumHeight: stickerPicker.stickerSize * 5
Layout.preferredHeight: stickersGridView.height
model: currentPack
cellWidth: stickerPicker.stickerSize + stickerPicker.stickerMargin
cellHeight: stickerPicker.stickerSize + stickerPicker.stickerMargin
- delegate: MouseArea {
+ focus: true
+ focusPolicy: Qt.StrongFocus
+ delegate: AbstractButton {
+ id: stickerButton
objectName: `sticker${index}`
property var sticker: currentPack.at(index)
height: stickerPicker.stickerSize + stickerPicker.stickerMargin
width: stickerPicker.stickerSize + stickerPicker.stickerMargin
+ topInset: 0
+ bottomInset: 0
+ leftInset: 0
+ rightInset: 0
+ padding: stickerPicker.stickerMargin
- Kazv.ImageAdapter {
- anchors.centerIn: parent
+ contentItem: Kazv.ImageAdapter {
fillMode: Image.PreserveAspectFit
height: stickerPicker.stickerSize
width: stickerPicker.stickerSize
mxcUri: sticker.mxcUri
}
- Rectangle {
- visible: hoverHandler.hovered
- z: -1
- anchors.fill: parent
+ hoverEnabled: true
+ background: Rectangle {
+ visible: stickerButton.hovered || stickerButton.visualFocus
color: Kirigami.Theme.activeBackgroundColor
}
- HoverHandler {
- id: hoverHandler
- }
+ Accessible.role: Accessible.Button
+ Accessible.name: sticker.body
+ Accessible.onPressAction: requestSendMessage()
+ onClicked: requestSendMessage()
- onClicked: stickerPicker.sendMessageRequested(sticker.makeEventJson())
+ function requestSendMessage() {
+ stickerPicker.sendMessageRequested(sticker.makeEventJson());
+ }
}
}
Component.onCompleted: {
console.log('stickerpicker finished', stickerPackList.count);
}
}
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 249810e..1c53144 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,110 +1,151 @@
set(KAZV_TEST_TEMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/test-temp)
configure_file(test-temp.hpp.in test-temp.hpp)
set(kazvtestlib_SRCS
test-model.cpp
)
add_library(kazvtestlib STATIC ${kazvtestlib_SRCS})
target_include_directories(kazvtestlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(kazvtestlib PUBLIC kazvprivlib libkazv::kazvtestfixtures)
add_executable(matrix-sdk-session-loader matrix-sdk-session-loader.cpp matrix-sdk-sessions-test.cpp)
target_compile_definitions(matrix-sdk-session-loader PRIVATE MATRIX_SDK_SESSIONS_TEST_NO_MAIN)
target_link_libraries(matrix-sdk-session-loader Qt${QT_MAJOR_VERSION}::Test kazvtestlib)
ecm_add_tests(
qt-job-handler-test.cpp
qt-promise-handler-test.cpp
qt-json-test.cpp
device-mgmt-test.cpp
matrix-sdk-test.cpp
matrix-sdk-sessions-test.cpp
matrix-promise-test.cpp
matrix-room-timeline-test.cpp
matrix-room-member-list-model-test.cpp
matrix-room-list-test.cpp
matrix-room-state-test.cpp
kazv-markdown-test.cpp
matrix-sticker-pack-test.cpp
matrix-link-test.cpp
matrix-user-given-attrs-map-test.cpp
kazv-abstract-list-model-test.cpp
matrix-event-list-test.cpp
matrix-event-test.cpp
kazv-file-test.cpp
db-store-test.cpp
sso-login-process-test.cpp
LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test kazvtestlib
)
ecm_add_test(
kazv-io-job-test.cpp
LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test Qt${QT_MAJOR_VERSION}::HttpServer kazvtestlib
)
ecm_add_test(
kazv-io-manager-test.cpp
LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test Qt${QT_MAJOR_VERSION}::HttpServer kazvtestlib
)
add_executable(quicktest quick-test.cpp)
target_link_libraries(quicktest PRIVATE Qt${QT_MAJOR_VERSION}::QuickTest kazvtestlib)
if(KAZV_BUILD_BENCHMARKS)
ecm_add_test(
matrix-room-timeline-benchmark-test.cpp
LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test kazvtestlib
)
endif()
set(quicktest_QMLS
tst_About.qml
tst_AddStickerPopup.qml
tst_AvatarImageAdapters.qml
tst_ConfirmUploadPopup.qml
tst_CreateRoomPage.qml
tst_DeviceList.qml
tst_DevicePopup.qml
tst_Device.qml
tst_EventHistoryView.qml
tst_EventReactions.qml
tst_EventReadIndicator.qml
tst_EventSourceView.qml
tst_EventView.qml
tst_EventViewRedacted.qml
tst_EventViewVideo.qml
tst_FileHandler.qml
tst_JoinRoomPage.qml
tst_KazvIOMenu.qml
tst_kazvshortcutsActionItem.qml
tst_LoginPage.qml
tst_MatrixHelpers.qml
tst_Notifications.qml
tst_PageManager.qml
tst_ReactToEventPopup.qml
tst_RoomInvitePage.qml
tst_RoomListViewItemDelegate.qml
tst_RoomMemberListPage.qml
tst_RoomNameProvider.qml
tst_RoomPage.qml
tst_RoomPinnedEventsPage.qml
tst_RoomSettingsPage.qml
tst_RoomStatePage.qml
tst_RoomStickerPackItemDelegate.qml
tst_RoomStickerPacksPage.qml
tst_RoomTimelineView.qml
tst_SendMessageBoxDrafts.qml
tst_SendMessageBox.qml
tst_StickerPackNameProvider.qml
- tst_StickerPicker.qml
tst_TypingIndicator.qml
tst_UserNameProvider.qml
tst_UserPage.qml
)
foreach(testfilename ${quicktest_QMLS})
message(STATUS "Adding quick test ${testfilename}")
string(REPLACE .qml "" testname ${testfilename})
add_test(NAME ${testname} COMMAND quicktest -input ${CMAKE_CURRENT_SOURCE_DIR}/quick-tests/${testfilename})
+ set_property(TEST ${testname}
+ PROPERTY LABELS "quicktest")
endforeach()
+
+if(kazv_ENABLE_APPIUM_TESTS)
+ qt_add_qml_module(kazvappiumcomponentqmlmodule
+ STATIC
+ URI moe.kazv.mxc.kazvappiumcomponent
+ VERSION 0.0
+ )
+
+ kazv_add_qml_files(kazvappiumcomponentqmlmodule
+ BASE_DIRECTORY run-appium-component-qml
+ SOURCES
+ Main.qml
+ )
+
+ add_executable(run-appium-component run-appium-component.cpp)
+ target_link_libraries(run-appium-component
+ PRIVATE
+ kazvappiumcomponentqmlmodule
+ kazvappiumcomponentqmlmoduleplugin
+ kazvtestlib
+ Qt::Quick
+ )
+
+ set(appium_tests_SOURCES
+ TestStickerPicker.py
+ )
+ foreach(testfilename ${appium_tests_SOURCES})
+ message(STATUS "Adding appium test ${testfilename}")
+ string(REPLACE .py "" testname ${testfilename})
+ add_test(NAME ${testname}
+ COMMAND
+ ${SELENIUM_WEBDRIVER_AT_SPI_RUN_PATH}
+ ${CMAKE_CURRENT_SOURCE_DIR}/appium-tests/${testfilename}
+ )
+ set_property(TEST ${testname}
+ PROPERTY ENVIRONMENT "KAZV_APPIUM_TEST_BIN=$<TARGET_FILE:run-appium-component>;TEST_WITH_VIDEO_RECORDER=0")
+ set_property(TEST ${testname}
+ PROPERTY LABELS "appiumtest")
+ endforeach()
+endif()
diff --git a/src/tests/appium-tests/README.md b/src/tests/appium-tests/README.md
new file mode 100644
index 0000000..1d5ff77
--- /dev/null
+++ b/src/tests/appium-tests/README.md
@@ -0,0 +1,10 @@
+
+# Appium tests
+
+Currently, only component tests are defined.
+
+The component test relies on the loader entrypoint (`../run-appium-component.cpp` and `../run-appium-component-qml/Main.qml`).
+
+Each component test contains two parts, a `.py` file and a `.qml` file. The `.py` file has a class inherited from `utils.ComponentTest`, which loads the corresponding `.qml` file with the same base name through the loader entrypoint.
+
+To add a test, create `<TestName>.py` and `<TestName>.qml` under this directory, and then add the `<TestName>.py` file to `appium_tests_SOURCES` variable in the `CMakeLists.txt` file in the parent directory.
diff --git a/src/tests/appium-tests/TestStickerPicker.py b/src/tests/appium-tests/TestStickerPicker.py
new file mode 100755
index 0000000..d60f690
--- /dev/null
+++ b/src/tests/appium-tests/TestStickerPicker.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# This file is part of kazv.
+# SPDX-FileCopyrightText: 2026 tusooa <tusooa@kazv.moe>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+from helpers import utils
+from appium.webdriver.common.appiumby import AppiumBy
+from selenium.webdriver.support.ui import WebDriverWait
+import json
+
+class StickerPickerTest(utils.ComponentTest):
+ def test_sticker_picker(self):
+ sticker0 = self.driver.find_element(by=AppiumBy.NAME, value='some')
+ sticker0.click()
+ self.driver.find_element(by=AppiumBy.NAME, value='spy-count=1')
+ expected = {
+ 'type': 'm.sticker',
+ 'content': {
+ 'body': 'some',
+ 'url': 'mxc://example.org/some',
+ },
+ }
+ self.wait_for(lambda: self.get_json_for('spy-last-arg') == expected)
+
+ pack1 = self.driver.find_element(by=AppiumBy.CLASS_NAME, value='[page tab | Pack 1]')
+ pack1.click()
+ sticker2 = self.driver.find_element(by=AppiumBy.NAME, value='some2')
+ sticker2.click()
+ self.driver.find_element(by=AppiumBy.NAME, value='spy-count=2')
+ expected = {
+ 'type': 'm.sticker',
+ 'content': {
+ 'body': 'some2',
+ 'url': 'mxc://example.org/some2',
+ },
+ }
+ self.wait_for(lambda: self.get_json_for('spy-last-arg') == expected)
+
+if __name__ == '__main__':
+ utils.main(__file__)
diff --git a/src/tests/quick-tests/tst_StickerPicker.qml b/src/tests/appium-tests/TestStickerPicker.qml
similarity index 54%
rename from src/tests/quick-tests/tst_StickerPicker.qml
rename to src/tests/appium-tests/TestStickerPicker.qml
index 428c46d..f35c72c 100644
--- a/src/tests/quick-tests/tst_StickerPicker.qml
+++ b/src/tests/appium-tests/TestStickerPicker.qml
@@ -1,120 +1,118 @@
/*
* This file is part of kazv.
- * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-FileCopyrightText: 2026 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-import QtQuick 2.15
-import QtQuick.Layouts 1.15
-import QtTest 1.0
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtTest
import '../../contents/ui' as Kazv
-import 'test-helpers.js' as JsHelpers
-import 'test-helpers' as QmlHelpers
-
-import moe.kazv.mxc.kazv 0.0 as MK
-
-QmlHelpers.TestItem {
- id: item
+import 'qml-helpers' as AppiumQmlHelpers
+ColumnLayout {
function makeSticker(sticker) {
return {
type: 'm.sticker',
content: {
body: sticker.body,
url: sticker.mxcUri,
info: sticker.info,
},
};
}
property list<ListModel> stickerPacks: [
ListModel {
id: stickerPack0
+ property string packName: 'Pack 0'
ListElement {
shortCode: 'some'
body: 'some'
mxcUri: 'mxc://example.org/some'
makeEventJson: () => makeSticker(stickerPack0.get(0))
}
ListElement {
shortCode: 'some1'
body: 'some1'
mxcUri: 'mxc://example.org/some1'
makeEventJson: () => makeSticker(stickerPack0.get(1))
}
function at(index) {
return stickerPack0.get(index);
}
},
ListModel {
id: stickerPack1
+ property string packName: 'Pack 1'
ListElement {
shortCode: 'some2'
body: 'some2'
mxcUri: 'mxc://example.org/some2'
makeEventJson: () => makeSticker(stickerPack1.get(0))
}
ListElement {
shortCode: 'some3'
body: 'some3'
mxcUri: 'mxc://example.org/some3'
makeEventJson: () => makeSticker(stickerPack1.get(1))
}
function at(index) {
return stickerPack1.get(index);
}
}
]
SignalSpy {
id: sendMessageRequestedSpy
signalName: 'sendMessageRequested'
}
+ function reset() {
+ sendMessageRequestedSpy.clear();
+ sendMessageRequestedSpy.target = stickerPicker;
+ spyLastArgLabel.value = spyLastArgLabel.getValue('');
+ }
+
+ AppiumQmlHelpers.InfoLabel {
+ tag: 'spy-count'
+ value: sendMessageRequestedSpy.count
+ }
+
+ AppiumQmlHelpers.InfoLabel {
+ id: spyLastArgLabel
+ Connections {
+ target: stickerPicker
+ function onSendMessageRequested(json) {
+ spyLastArgLabel.value = spyLastArgLabel.getValue(json);
+ }
+ }
+ tag: 'spy-last-arg'
+ value: getValue('')
+
+ function getValue(json) {
+ return JSON.stringify(json);
+ }
+ }
+
Kazv.StickerPicker {
+ Layout.fillHeight: true
id: stickerPicker
stickerPackList: ListModel {
id: stickerPackListModel
ListElement {
}
ListElement {
}
function at(index) {
return stickerPacks[index];
}
}
}
-
- TestCase {
- id: stickerPickerTest
- name: 'StickerPickerTest'
- when: windowShown
-
- function init() {
- sendMessageRequestedSpy.clear();
- sendMessageRequestedSpy.target = stickerPicker;
- }
-
- function test_stickerPicker() {
- verify(findChild(stickerPicker, 'stickerPack0'));
- verify(findChild(stickerPicker, 'stickerPack1'));
- verify(findChild(stickerPicker, 'sticker0'));
- verify(findChild(stickerPicker, 'sticker1'));
- const stickerButton = findChild(stickerPicker, 'sticker1');
- mouseClick(stickerButton);
- tryVerify(() => sendMessageRequestedSpy.count === 1, 1000);
- verify(JsHelpers.deepEqual(sendMessageRequestedSpy.signalArguments[0][0], makeSticker(stickerPack0.get(1))));
-
- mouseClick(findChild(stickerPicker, 'stickerPack1'));
- tryVerify(() => findChild(stickerPicker, 'sticker0').sticker.mxcUri === 'mxc://example.org/some2');
- mouseClick(findChild(stickerPicker, 'sticker0'));
- tryVerify(() => sendMessageRequestedSpy.count === 2, 1000);
- verify(JsHelpers.deepEqual(sendMessageRequestedSpy.signalArguments[1][0], makeSticker(stickerPack1.get(0))));
- }
- }
}
diff --git a/src/tests/appium-tests/helpers/__init__.py b/src/tests/appium-tests/helpers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/tests/appium-tests/helpers/utils.py b/src/tests/appium-tests/helpers/utils.py
new file mode 100644
index 0000000..f5d95ef
--- /dev/null
+++ b/src/tests/appium-tests/helpers/utils.py
@@ -0,0 +1,62 @@
+# This file is part of kazv.
+# SPDX-FileCopyrightText: 2026 tusooa <tusooa@kazv.moe>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+import unittest
+from appium import webdriver
+from appium.options.common.base import AppiumOptions
+from appium.webdriver.common.appiumby import AppiumBy
+import sys
+import os
+import json
+import time
+
+tests_dir = None
+program_name = None
+module_name = None
+
+class TestUtilMixin:
+ def get_json_for(self, name):
+ elem = self.driver.find_element(by=AppiumBy.XPATH, value=f'//*[starts-with(@name, "{name}=")]')
+ print(elem, file=sys.stderr)
+ attr = elem.get_attribute('name')
+ print(attr, file=sys.stderr)
+ return json.loads(attr[len(name) + 1:])
+
+ def wait_for(self, pred, timeout_ms=10000, retry_ms=100):
+ steady_deadline = time.monotonic() + timeout_ms / 1000
+ satisfied = False
+ while time.monotonic() < steady_deadline:
+ if pred():
+ satisfied = True
+ break
+ time.sleep(retry_ms / 1000)
+ self.assertTrue(satisfied, msg=f'Predicate {pred} does not return truthy within {timeout_ms}ms')
+
+class ComponentTest(unittest.TestCase, TestUtilMixin):
+ @classmethod
+ def setUpClass(cls):
+ options = AppiumOptions()
+ print('----program name:', program_name, file=sys.stderr)
+ print('----tests dir:', tests_dir, file=sys.stderr)
+ print('----module name:', module_name, file=sys.stderr)
+ options.set_capability("app", f'env {program_name} {tests_dir}/{module_name}.qml')
+ cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)
+ cls.driver.implicitly_wait = 10
+
+ def setUp(self):
+ self.driver.find_element(by=AppiumBy.NAME, value='appium-component-reset').click()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.driver.quit()
+
+def main(filename):
+ global tests_dir, program_name, module_name
+ tests_dir = os.path.dirname(filename)
+ program_name = os.environ.get(
+ 'KAZV_APPIUM_TEST_BIN',
+ os.path.join(os.getcwd(), 'bin', 'run-appium-component')
+ )
+ module_name, _ext = os.path.splitext(os.path.basename(filename))
+ unittest.main()
diff --git a/src/tests/appium-tests/qml-helpers/InfoLabel.qml b/src/tests/appium-tests/qml-helpers/InfoLabel.qml
new file mode 100644
index 0000000..663ab70
--- /dev/null
+++ b/src/tests/appium-tests/qml-helpers/InfoLabel.qml
@@ -0,0 +1,14 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2026 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import QtQuick
+import QtQuick.Controls
+
+Label {
+ property string tag
+ property string value
+ text: `${tag}=${value}`
+}
diff --git a/src/tests/run-appium-component-qml/Main.qml b/src/tests/run-appium-component-qml/Main.qml
new file mode 100644
index 0000000..1599082
--- /dev/null
+++ b/src/tests/run-appium-component-qml/Main.qml
@@ -0,0 +1,41 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2026 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+ApplicationWindow {
+ id: root
+ property string componentToLoad: ''
+ title: 'run-appium-component'
+ width: 1000
+ height: 800
+ visible: true
+
+ header: ToolBar {
+ RowLayout {
+ Button {
+ text: 'appium-component-reset'
+ onClicked: root.reset()
+ }
+ }
+ }
+
+ Loader {
+ id: loader
+ source: root.componentToLoad
+ anchors.fill: parent
+ }
+
+ function reset() {
+ if (loader.item && loader.item.reset) {
+ loader.item.reset();
+ } else {
+ console.log('Reset is not defined by loader item');
+ }
+ }
+}
diff --git a/src/tests/run-appium-component.cpp b/src/tests/run-appium-component.cpp
new file mode 100644
index 0000000..0c8dd50
--- /dev/null
+++ b/src/tests/run-appium-component.cpp
@@ -0,0 +1,29 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2026 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <QQmlApplicationEngine>
+#include <QGuiApplication>
+#include <QFileInfo>
+
+using namespace Qt::Literals::StringLiterals;
+
+Q_DECL_EXPORT int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+ QQmlApplicationEngine engine;
+ engine.loadFromModule(u"moe.kazv.mxc.kazvappiumcomponent"_s, u"Main"_s);
+
+ if (engine.rootObjects().isEmpty()) {
+ return -1;
+ }
+
+ auto window = engine.rootObjects().at(0);
+ qDebug() << "Loading component" << argv[1];
+ auto fileInfo = QFileInfo(QString::fromLocal8Bit(argv[1]));
+ window->setProperty("componentToLoad", QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
+
+ return app.exec();
+}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Oct 12, 4:35 AM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1786047
Default Alt Text
(37 KB)

Event Timeline