Code Monkey home page Code Monkey logo

spix's Introduction

Build and Test

Spix Logo

Spix

Spix is a minimally invasive UI testing library that enables your Qt/QML app's UI to be controlled either via c++ code, or through an http RPC interface.

UI elements are referenced through names and paths which are robust against design changes. To click on a button you write

mouseClick("mainWindow/ok_button");

To provide an RPC test interface to your app, only add these three lines to your main(...) function:

spix::AnyRpcServer server;
auto bot = new spix::QtQmlBot();
bot->runTestServer(server);

And a test script in python could look like this:

import xmlrpc.client

s = xmlrpc.client.ServerProxy('http://localhost:9000')
s.mouseClick("mainWindow/Button_1")
s.wait(200)
s.mouseClick("mainWindow/Button_2")
resultText = s.getStringProperty("mainWindow/results", "text")
s.quit()

You can also use PyAutoGUI in combination with Spix. Have a look at the example script.

What are the applications of Spix?

The main use for Spix is to automatically test the UI of your Qt/QML application and make sure that it behaves as you expect. However, you can also use Spix as an easy way to remote control existing Qt/QML applications or to automatically generate and update screenshots for your documentation.

Requirements

  • Qt (both 5 and 6 supported)
  • AnyRPC

Current Features

  • Send mouse events (click, move, drag/drop)
  • Drop mime data from external apps
  • Enter text
  • Check existence and visibility of items
  • Get property values of items (text, position, color, ...)
  • Invoke a method on an object
  • Take and save a screenshot
  • Quit the app
  • Remote control, also of embedded devices / iOS

Setting Up Spix

Installing AnyRPC

Spix requires AnyRPC be installed on the local machine before building. For *nix machines, AnyRPC can be built/installed using CMake:

# in a temporary directory
git clone https://github.com/sgieseking/anyrpc.git
cd anyrpc
mkdir build
cd build
cmake -DBUILD_EXAMPLES=OFF -DBUILD_WITH_LOG4CPLUS=OFF -DBUILD_PROTOCOL_MESSAGEPACK=OFF ..
cmake --build .
sudo cmake --install .

For non-*nix machines, checkout the CI install script.

Installing Spix

Spix uses cmake and can be build with the standard cmake commands once cloned:

git clone https://github.com/faaxm/spix
cd spix
mkdir build && cd build
cmake -DSPIX_QT_MAJOR=6 ..
cmake --build .
sudo cmake --install .

Change SPIX_QT_MAJOR to 5 to build against Qt5 instead of Qt6.

If you installed the dependencies (like AnyRPC) in a non-standard directory you can point cmake to it by setting CMAKE_PREFIX_PATH, so instead of cmake .. you run:

cmake -DCMAKE_PREFIX_PATH=/path/to/libs ..

Including Spix in your Qt project

If using qmake, add the following to your Qt .pro file:

QT += quick
INCLUDEPATH += /usr/local/include
LIBS += -lSpix -lanyrpc

If using CMake, add the following to your CMakeLists.txt:

find_package(Spix REQUIRED)

Update your main(...) to start the Spix RPC server:

#include <Spix/AnyRpcServer.h>
#include <Spix/QtQmlBot.h>

int main(...) {
    ...
    spix::AnyRpcServer server;
    auto bot = new spix::QtQmlBot();
    bot->runTestServer(server);
    ...
}

Finally, if you're using a QQuickView as your root window, you'll need to give it an object name in your main (otherwise the root window object name will be defined in your QML):

int main(...) {
    QQuickView view;
    view.setObjectName("root")
    ...
}

Using Spix

The easiest method of interacting with Spix is using the XMLRPC client built into python:

import xmlrpc.client

s = xmlrpc.client.ServerProxy('http://localhost:9000') # default port is 9000
s.method(<path>, <options>)
# for example:
s.mouseClick("root/Button_2")
resultText = s.getStringProperty("root/results", "text")

You can also use the XMLRPC client to list the available methods. The complete list of methods are also available in the source.

print(s.system.listMethods())
# ['command', 'enterKey', 'existsAndVisible', 'getBoundingBox', 'getErrors', 'getStringProperty', 'inputText', 'invokeMethod', 'mouseBeginDrag', 'mouseClick', 'mouseDropUrls', 'mouseEndDrag', 'quit', 'setStringProperty', 'system.listMethods', 'system.methodHelp', 'takeScreenshot', 'wait']
print(s.system.methodHelp('mouseClick'))
# Click on the object at the given path

Spix uses a slash-separated path format to select Qt objects. Selectors match against objectName or id if no object name is defined.

<root>/<child0>(/<childN>...)

Spix matches children recursivley, allowing as much flexibility as needed:

# matches any `button` that is a descendant of `root` (even subchildren)
'root/button'
# matches any `button` that is a descendant of `numberpad` which is in turn a descendant of `root`.
'root/numberpad/button'
# and so on

More specifically, Spix's matching processes works as follows:

  • <root> matches a top-level QQuickWindow whose objectName (or id if objectName is empty) matches the specified string. Top-level windows are enumerated by QGuiApplication::topLevelWindows.
  • <child> matches the first child object whose objectName (or id if objectName is empty) matches the specified string using a recursive search of all children and subchildren of the root. This process repeats for every subsequent child path entry.

Invoking QML methods

Spix can directly invoke both internal and custom methods in QML objects: this can be a handy way to automate interactions that Spix doesn't support normally. For example, we can control the cursor in a TextArea by calling TextArea.select:

TextArea {
    id: textArea
}
# select characters 100-200
s.invokeMethod("root/textArea", "select", [100, 200])

In addition, you can use custom functions in the QML to implement more complicated interactions, and have Spix interact with the function:

TextArea {
    id: textArea
    function customFunction(arg1, arg2) {
        // insert QML interactions here
        return {'key1': true, 'key2': false}
    }
}
# invoke the custom function
result = s.invokeMethod("root/textArea", "customFunction", ['a string', 34])
# prints {'key1': True, 'key2': False}
print(result)

Spix supports the following types as arguments/return values:

Python Type XMLRPC Type QML Type(s) JavaScript Type(s) Notes
int <int> int number Values over/under int max are upcasted to double
bool <boolean> bool boolean
str <string> string string
float <double> double, real number Defaults to double
datetime.datetime <dateTime.iso8601> date Date No timezone support (always uses local timezone)
dict <struct> var object String keys only
list <array> var Array
None no type null, undefined object, undefined Defaults to null

In general Spix will attempt to coerce the arguments and return value to the correct types to match the method being invoked. Valid conversion are listed under the QVariant docs. If Spix cannot find a valid conversion it will generate an error.

Item {
    id: item
    function test(arg1: bool) {
        ...
    }
}
# ok
s.invokeMethod("root/item", "test", [False])

# argument will implicitly be converted to a boolean (True) to match the declaration type
s.invokeMethod("root/item", "test", [34])

# no conversion from object to boolean, so an error is thrown
s.invokeMethod("root/item", "test", [{}])

Two modes of operation

In general, Spix can be used in two ways, which are different in how events are generated and sent to your application:

Generate Qt events directly

You can use Spix to directly create Qt events, either from C++ as a unit test, or from an external script via the network through RPC. Since the Qt events are generated directly inside the app, and do not come from the system, the mouse cursor will not actually move and interaction with other applications is limited. On the plus side, this mechanism is independent from the system your app is running on and can easily be used to control software on an embedded device via the network (RPC).

Generate system events externally

In this case, Spix is not generating the events itself. Instead, you use a script to query Spix for the screen coordinates of qt objects and then generate events on the system level through other tools. One option is to use python together with PyAutoGUI for this, as is done in the RemoteCtrl example.

spix's People

Contributors

ctmbl avatar faaxm avatar krussjos avatar lubosd avatar m-kuhn avatar mablanchard avatar melodicspavol avatar prototypicalpro avatar ramamario avatar tueksta avatar waleedyaser avatar walterbrebels avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spix's Issues

Get items by id

Thank you for this very nice library, I am just testing it for https://github.com/opengisch/QField/

One place where I could see some potential for improvement is by allowing to use id's instead of objectName's, plenty of the code already defines ids, but almost nowhere objectNames, so the overhead for writing tests could be lowered.

What I think would need to be done:

Happy to hear back any feedback on this.

Run UI/integration tests as workflow

It would be nice to have some automated tests that check if Spix is working as expected with different Qt versions and when the code is changed. As it is hard to cover the interaction between Spix and Qt with unit tests one way would be to have a suite of UI tests running and verifying that they deliver consistent results.

This could be just having a github workflow run the examples that come with Spix and evaluating their results, or something more compact / customized.

Find by unique object name.

Hi dear @faaxm
Thanks for creation of this useful and beautiful thing. ❀️ πŸ‘ 🌹
How to find items by a unique object name.
It is really hard to access to an item by its full path. ("mainWidnow/mainView/rightSide/setting")
I have some items that are creating by another ones in runtime Unpredictably and they don't have any predefined area.
I don't now to handle some situations like this.
But if there is a way to access qml items by unique object name i think this problem will be solved easily.

Error while creating instance

I have successfully built and installed in MacOS. But when I tried to make an instance the following error occurs

Undefined symbols for architecture x86_64:
  "anyrpc::Connection::Connection(int, anyrpc::MethodManager*)", referenced from:
      anyrpc::HttpConnection::HttpConnection(int, anyrpc::MethodManager*, std::__1::vector<anyrpc::RpcContentHandler, std::__1::allocator<anyrpc::RpcContentHandler> >&) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Connection::~Connection()", referenced from:
      anyrpc::HttpConnection::HttpConnection(int, anyrpc::MethodManager*, std::__1::vector<anyrpc::RpcContentHandler, std::__1::allocator<anyrpc::RpcContentHandler> >&) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::MethodManager::AddMethod(anyrpc::Method*)", referenced from:
      void spix::utils::AddFunctionToAnyRpc<void (int)>(anyrpc::MethodManager*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (int)>) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::AddFunctionToAnyRpc<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>(anyrpc::MethodManager*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::AddFunctionToAnyRpc<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)>(anyrpc::MethodManager*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)>) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::AddFunctionToAnyRpc<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>(anyrpc::MethodManager*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::AddFunctionToAnyRpc<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, unsigned int)>(anyrpc::MethodManager*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, unsigned int)>) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::AddFunctionToAnyRpc<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>(anyrpc::MethodManager*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::AddFunctionToAnyRpc<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>(anyrpc::MethodManager*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>) in libSpix.a(AnyRpcServer.cpp.o)
      ...
  "anyrpc::MethodManager::~MethodManager()", referenced from:
      anyrpc::Server::~Server() in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::XmlRpcHandler(anyrpc::MethodManager*, char*, unsigned long, anyrpc::Stream&)", referenced from:
      anyrpc::XmlHttpServer::XmlHttpServer() in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::PushBack(anyrpc::Value&)", referenced from:
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::function<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > ()>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::SetArray()", referenced from:
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::function<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > ()>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::Value(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", referenced from:
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::function<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > ()>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::~Value()", referenced from:
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::function<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > ()>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::operator=(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)", referenced from:
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>, anyrpc::Value&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::operator=(bool)", referenced from:
      void spix::utils::callAndAssignAnyRpcResult<bool, void, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::function<bool (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>, anyrpc::Value&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::operator[](unsigned long)", referenced from:
      std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > spix::utils::unpackAnyRpcParam<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::callAndAssignAnyRpcResult<int, 0ul, std::__1::function<void (int)> >(std::__1::function<void (int)>, anyrpc::Value&, std::__1::integer_sequence<unsigned long, 0ul>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 0ul, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)> >(std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>, anyrpc::Value&, std::__1::integer_sequence<unsigned long, 0ul>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, 0ul, 1ul, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)> >(std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)>, anyrpc::Value&, std::__1::integer_sequence<unsigned long, 0ul, 1ul>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 0ul, 1ul, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)> >(std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>, anyrpc::Value&, std::__1::integer_sequence<unsigned long, 0ul, 1ul>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, unsigned int, 0ul, 1ul, 2ul, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, unsigned int)> >(std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, int, unsigned int)>, anyrpc::Value&, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      void spix::utils::callAndAssignAnyRpcResult<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, 0ul, 1ul, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)> >(std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)>, anyrpc::Value&, std::__1::integer_sequence<unsigned long, 0ul, 1ul>, anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      ...
  "anyrpc::Server::AddHandler(bool (*)(anyrpc::MethodManager*, char*, unsigned long, anyrpc::Stream&), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)", referenced from:
      anyrpc::XmlHttpServer::XmlHttpServer() in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Server::StartThread()", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Server::BindAndListen(int, int)", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Server::AddAllHandlers()", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Server::Exit()", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Server::Server()", referenced from:
      anyrpc::ServerST::ServerST() in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Socket::~Socket()", referenced from:
      anyrpc::TcpSocket::~TcpSocket() in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::ServerST::Work(int)", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::ServerST::Shutdown()", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::internal::HttpHeader::HttpHeader()", referenced from:
      anyrpc::internal::HttpRequest::HttpRequest() in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Value::GetString() const", referenced from:
      std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > spix::utils::unpackAnyRpcParam<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
      std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > spix::utils::unpackAnyRpcParam<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(anyrpc::Value&) in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Server::GetConnectionsPeerInfo(std::__1::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::list<unsigned int, std::__1::allocator<unsigned int> >&) const", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Server::GetConnectionsSockInfo(std::__1::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::list<unsigned int, std::__1::allocator<unsigned int> >&) const", referenced from:
      vtable for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "anyrpc::Socket::GetSockInfo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, unsigned int&) const", referenced from:
      anyrpc::Server::GetMainSockInfo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, unsigned int&) const in libSpix.a(AnyRpcServer.cpp.o)
  "typeinfo for anyrpc::ServerST", referenced from:
      typeinfo for anyrpc::XmlHttpServer in libSpix.a(AnyRpcServer.cpp.o)
  "vtable for anyrpc::HttpConnection", referenced from:
      anyrpc::HttpConnection::HttpConnection(int, anyrpc::MethodManager*, std::__1::vector<anyrpc::RpcContentHandler, std::__1::allocator<anyrpc::RpcContentHandler> >&) in libSpix.a(AnyRpcServer.cpp.o)
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "vtable for anyrpc::Server", referenced from:
      anyrpc::Server::~Server() in libSpix.a(AnyRpcServer.cpp.o)
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "vtable for anyrpc::ServerST", referenced from:
      anyrpc::ServerST::ServerST() in libSpix.a(AnyRpcServer.cpp.o)
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
  "vtable for anyrpc::internal::HttpRequest", referenced from:
      anyrpc::internal::HttpRequest::HttpRequest() in libSpix.a(AnyRpcServer.cpp.o)
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [TestApp.app/Contents/MacOS/TestApp] Error 1

Application .pro file

macx: LIBS += -L$$PWD/../../../usr/local/lib/ -lSpix

INCLUDEPATH += $$PWD/../../../usr/local/include
DEPENDPATH += $$PWD/../../../usr/local/include

macx: PRE_TARGETDEPS += $$PWD/../../../usr/local/lib/libSpix.a

ListView QML

what should be the approach to mouseClick an item from a ListView QML item that uses a model and a delegate?

Create 0.6 release

Hello,

I have currently #87 which has been fix by this merge #91.
I need to get spix from conan which don't have this fix,
Is there a possibility to tag the main branch with a new version like 0.6 or 0.51 ?
Next, I'm in contact with @fdgStilla to add the new spix receipe in conan-center

Thanks for your work and your help

Escape character for '/' as a directory seperator?

With the existing implementation of TestServer.cpp, would it be possible to search for a path that contains a '/' without treating the '/' as a directory seperator? e.g. 'mainWindow/1/2' where '1/2' is the object to find

If not, what modifications could be done to the existsAndVisible function?

QML Popup and children are invisible to Spix name/id lookup

Spix's item lookup fails to find and QML Item of Popup type or children of it (even when it is visible/open).
e.g.

Popup {
  id: mypopup
  Rectangle {
    id: myrect
  }
}

Spix isn't able to find mypopup or myrect.

We uses Popups for various kinds of dialog questions with buttons in our app and Spix isn't able to click on any of the dialog buttons or even find the Popup itself to determine visibility (when the popups are open), so we can't even add helper functions to click the buttons etc.

The Qt docs talk about some kind of dynamic re-parenting and there are properties contentItem, contentChildren and contentData (but no children as it inherits from QtObject not Item, which does have an objectName).

Any ideas where to look troubleshooting or seeing how to add support for that?

Support for mouse position/hover

I stumbled upon the use-case where I need to check the correct behavior of the app under test when using mouse hover over an element (i.e. tooltips).
Is this planned and/or something of interest if I would implement it for my team?

Compile error Ubuntu 18.04

Code will not compile on Ubuntu 18.04 due to missing #include <atomic> in AnyRpcServer.cpp. Adding the include solves this

  • updated comment

Generic Callback

Hi,

I have the use-case where the application under test shall be fed by the external test script using the AnyRPC with some initial backend state.
So I quickly cooked up a generic callback implementation where one can send arbitrary data to the application under test.

So basically, the application under test would define a callback like so:

server.setGenericCommandHandler([](std::string command, std::string payload) {
   // do whatever needs to be done
});

and the script would feed in my case the initial state to the application before doing the usual stuff:

s = xmlrpc.client.ServerProxy('http://localhost:9000')
s.command('loadBackendState', 'BackendData')

Would this generally be a feature of interest?

Supported OS

I need to do test automation on Windows OS. Does the framework support Windows 10?

Cmake not working anymore on Linux

Hi,
Thanks for your very good library. The last commits (cmake-cleanup) did break something.
On an Ubuntu 18.04, ti works well with commit 071eeba, but not with the last one (00cf734 )

I got this message during "cmake ..":

CMake Error at CMakeLists.txt:39 (install):
install TARGETS given target "Spix" which does not exist in this directory.

-- Configuring incomplete, errors occurred!

Thanks for your nice job :-)

How to use the spix automatic testing tool

Hello, I want to ask why I put the instance code into the QT project. The cpp file is missing the "spix_export. h" header file, and cannot recognize many class names and function names, such as TestServer, override, wait, getStringProperty, QtQmlBot, etc.

Sending additional commands after 'quit' command

More a theoretical problem than anything else, but if you would send a 'quit' to the application and then enqueue some additional commands, before the quit is processed, what would happen?
I guess it is undefined behavior, crash or deadlock or something, no?

Can we have an additional lock or something to prevent any additional commands after a 'quit' was committed?

Strange crash - anyRPC socket closed in special case

Hello,

This is for now more a question as and issue.

We are using spix v0.3 and I also tried v0.5 and am getting the same problem.

We integrated spxi into the qt app as described in the readme and are using python for testing ([XMLRPC client built into python]).

Our system:

  • windows 10
  • Qt v5.15.2
  • using visual studio 2022 + qt creator to use MSVC2019 build toolchain.
  • using python v3.10

How we are testing:

  • Our Qt app integrates the spix and also open additional TCP socket for specific user commands/events.
  • we use spix commands/function to execute the python tests.

Our UI application has backend written in C++ and forehand in QML and we are using approaches like:

header file:

 `Q_PROPERTY( int testStep READ testStep NOTIFY testStepChanged )`

qml file:

    ...
    property int guideStep:                         TestSetupCtrl.testStep

    ...

    onGuideStepChanged:                             handleGuideControll()

    ...

    function handleGuideControll()
    {
//        console.log("simple logging ")

        switch (guideStep)
        {
            case TestSetupCtrl.QC_SWAP_GUIDE_STEP_PRECAUTIONS:
                guideControll.guideHeaderLabelText  = qsTr("PRECAUTIONS")
                guideControll.guideLabelText        = qsTr("Wear gloves when running a QC test")
                break
    ...

We then added python test cases using spix commands wait_screen, mouseclick, etc. And if we execute mouseclick() which simulates ui button click that changes testStep property then with the above code our test case fails, UI crash. But if I uncomment the above line "console.log" then test executes ok. Is there some timing problem here or did I miss something else? And did you encounter this issue on your side? Let me know if you need more information.

Thanks for the answer, Frenk

QQuickWidget support

Do you guys consider adding support for qml running in widgets? We use QMainWindow with QQuickWidget as a central widget for a couple of reasons and so far Spix cannot automate such applications.

After some digging I ended up with a working version with just a few lines of code, so maybe you can add that? You will need to add Qt5::Widgets and Qt5::QuickWidgets to the project, then add a widget-searching function:

QQuickItem* getQuickWidgetItemAtPath(const spix::ItemPath& path)
{
	QList<QQuickItem *> list;
	auto windows = qApp->topLevelWidgets();
	for (const auto& window : windows) {
		auto qwidgets = window->findChildren<QQuickWidget *>();
		for (auto qw : qwidgets) {
			list << qobject_cast<QQuickWidget *>(qw)->rootObject();
		}
	}

	for (auto qi : list) {
		auto res = getQQuickItemWithRoot(path, qi);
		if (res) return res;
	}

	return nullptr;
}

that you call in QQuickItem* getQQuickItemAtPath(const spix::ItemPath& path) in its "nothing found" branch:

if (!itemWindow) {
        return getQuickWidgetItemAtPath(path);
}

finally, it needs two includes at the top:

#include <QQuickWidget>
#include <QApplication>

as you would probably make widget support optional, I am not sure if I can submit a proper pull request that takes this into account, but you are very welcome to add the code above to the project, so I can use the original version.

Thank you :)

How to access elements in a dropdown list?

In QML if I have a combobox e.g.:

ComboBox {
    objectName: "fruitCombo"
    anchors.bottom: parent.bottom
    width: 200
    model: [ "Banana", "Apple", "Coconut" ]
}

I can click the list using mouseClick("fruitCombo") but how can I click individual items in the list? e.g. the 3rd item or the item with text 'Coconut'. Would such functionality be easy to implement in spix?

Won't compile with qt 6.5

Error (from VCPKG CI):

D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(98): error C2440: 'return': cannot convert from 'QMetaMethodReturnArgument' to 'QGenericReturnArgument'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(98): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(101): error C2440: 'return': cannot convert from 'QMetaMethodReturnArgument' to 'QGenericReturnArgument'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(101): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(104): error C2440: 'return': cannot convert from 'QMetaMethodReturnArgument' to 'QGenericReturnArgument'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(104): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(107): error C2440: 'return': cannot convert from 'QMetaMethodReturnArgument' to 'QGenericReturnArgument'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(107): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(110): error C2440: 'return': cannot convert from 'QMetaMethodReturnArgument' to 'QGenericReturnArgument'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(110): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(113): error C2440: 'return': cannot convert from 'QMetaMethodReturnArgument' to 'QGenericReturnArgument'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(113): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(116): error C2440: 'return': cannot convert from 'QMetaMethodReturnArgument' to 'QGenericReturnArgument'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(116): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(201): warning C4996: 'QVariant::canConvert': was declared deprecated
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(211): warning C4996: 'QVariant::canConvert': was declared deprecated
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(221): warning C4996: 'QVariant::canConvert': was declared deprecated
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(259): warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(260): warning C4996: 'QVariant::canConvert': was declared deprecated
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(271): warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(286): warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): error C2665: 'std::vector<QGenericArgument,std::allocator<QGenericArgument>>::push_back': no overloaded function could convert all the argument types
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.35.32215\include\vector(939): note: could be 'void std::vector<QGenericArgument,std::allocator<QGenericArgument>>::push_back(_Ty &&)'
        with
        [
            _Ty=QGenericArgument
        ]
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): note: 'void std::vector<QGenericArgument,std::allocator<QGenericArgument>>::push_back(_Ty &&)': cannot convert argument 1 from 'QMetaMethodArgument' to '_Ty &&'
        with
        [
            _Ty=QGenericArgument
        ]
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): note: Reason: cannot convert from 'QMetaMethodArgument' to '_Ty'
        with
        [
            _Ty=QGenericArgument
        ]
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.35.32215\include\vector(935): note: or       'void std::vector<QGenericArgument,std::allocator<QGenericArgument>>::push_back(const _Ty &)'
        with
        [
            _Ty=QGenericArgument
        ]
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): note: 'void std::vector<QGenericArgument,std::allocator<QGenericArgument>>::push_back(const _Ty &)': cannot convert argument 1 from 'QMetaMethodArgument' to 'const _Ty &'
        with
        [
            _Ty=QGenericArgument
        ]
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): note: Reason: cannot convert from 'QMetaMethodArgument' to 'const _Ty'
        with
        [
            _Ty=QGenericArgument
        ]
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(291): note: while trying to match the argument list '(QMetaMethodArgument)'
D:\buildtrees\spix\src\v0.5-97159dbd3f.clean\lib\src\Scene\Qt\QtItemTools.cpp(288): warning C4996: 'QVariant::convert': was declared deprecated

Can't run examples after built

I have ran "install-deps.sh" in /ci (I delete the --config in install-deps.sh )
Then I build spix with cmake-3.23.0-rc2,using the following command:

export CMAKE_PREFIX_PATH=/my/qt/path
mkdir build && cd build
cmake ..
cmake --build . --target install

After that, I open the RemoteCtrl in qtcreator and try to debug it
Then I got a serious errors:

πŸ‘Ž error: /usr/local/lib/x86_64-linux-gnu/libSpix.a(AnyRpcServer.cpp.o): in function std::__cxx11::basic_string<char, >std::char_traits<char>, std::allocator<char> > spix::utils::unpackAnyRpcParam<std::__cxx11::basic_string<char, >std::char_traits<char>, std::allocator<char> > >(anyrpc::Value&)': AnyRpcServer.cpp:-1: error: undefined reference to anyrpc::Value::GetString() const'
...
...


and many other errors like that says cannot find xxx.cpp in some other qt modules
I have installed both QT and AnyRpc,but it looks like I didn't confirm them well.
Is there a more more detailed description for the installation such as how to set the parameters,which ubuntu/qt/cmake version should I use?

And,as you can see, i'm a noob both in coding and English
I hope my question doesn't offend you

[Qt6] Drawer element blocks mouseClick

I have a strange bug that I'm trying to wrap my head around. When using spix (modified to print the mouse events) with a minimal QML application on Qt6 everything works as expected:

ApplicationWindow {
    id: window
    visible: true

    Button {
        id: clickMe
        onClicked: console.log("clicked");
        text: "A Special Button"
    }
}
s = xmlrpc.client.ServerProxy('http://localhost:9000')
s.mouseClick("window/clickMe")
print(s.getErrors())
# Qt
Starting minimalDrawerBug...
QML debugging is enabled. Only use this in a safe environment.
QMouseEvent(MouseButtonPress LeftButton pos=60.1016,12.5 scn=60.1016,12.5 gbl=2470,944 dev=QPointingDevice("core pointer" Mouse id=1))
QMouseEvent(MouseButtonRelease LeftButton pos=60.1016,12.5 scn=60.1016,12.5 gbl=2470,944 dev=QPointingDevice("core pointer" Mouse id=1))
qml: clicked

# python
[]

When I update the QML application to include a Drawer element, however, spix is unable to click on the element:

ApplicationWindow {
    id: window
    visible: true

    Button {
        id: clickMe
        onClicked: console.log("clicked");
        text: "A Special Button"
    }
    
    // I tested placing drawer both before and after button
    Drawer {
        id: drawer
    }
}
# Qt
Starting minimalDrawerBug...
QML debugging is enabled. Only use this in a safe environment.
QMouseEvent(MouseButtonPress LeftButton pos=60.1016,12.5 scn=60.1016,12.5 gbl=2470,944 dev=QPointingDevice("core pointer" Mouse id=1))
QMouseEvent(MouseButtonRelease LeftButton pos=60.1016,12.5 scn=60.1016,12.5 gbl=2470,944 dev=QPointingDevice("core pointer" Mouse id=1))

# python
[]

I am still able to click the button with normal mouse interaction, but spix seems unable to replicate the same behavior using synthetic events. As far as I can tell spix is generating the same MouseEvent in both cases, so maybe it has something to do with how Drawer interacts with mouse events?

I'm on Ubuntu 20.04, Qt 6.2.3 and the latest spix from master (modified slightly to print mouse events). I'd be happy to post the full project that I test with if that's helpful.

getStringProperty with compound properties

Given the following element:
Text { text: "Hello World!" objectName: "test" font.family: "Helvetica" font.pointSize: 24 }

How would one proceed to query the font family with getStringProperty(...)?

I tried the following approaches and none seems to return anything:

getStringProperty("test", "font.family")

getStringProperty("test/font", "family")

Am I doing something wrong or is there an issue?

Mouse right click

Hi,

I would like to be able to use mouse right click on an item.
It seems to me, that this is currently not possible, yes?

I could do the implementation if you can give me pointers if you would rather have it as a separate command or somehow amalgamated into the existing mouseClick.

Thanks.

Double click ?

Hi, first of all, great library ! I was wondering if it was possible to do a double click on a QML component ? I tried doing two successive mouseclick but it does not register as a doubleClick. I also tried with a wait between the two but it also doesn't register.

mouseBeginDrag and mouseEndDrag don't work in some cases

Hello,

I'm trying to integrate spix into my tests, but for some reason neither mouseBeginDrag nor mouseEndDrag move my mouse. Both functions activate drag mode, but if the mouse doesn't move on its own, it won't reach the target and therefore it doesn't actually accomplish anything.

It's weird because lib/src/Commands/DragBegin.cpp and lib/src/Commands/DragEnd.cpp already contain the following line:

env.scene().events().mouseMove(item.get(), midPoint);

OS: Arch Linux
Confirmed on X11 and Wayland
(QCursor::setPos doesn't work on Wayland anyway, therefore it can probably be ignored)

Edit: Maybe it's relevant that the objectNames I'm targetting are inside delegates. For the application, I'm using QGuiApplication and QQmlApplicationEngine, too. Simple clicking seems to work just fine (double click doesn't though, but I suspect that this would have to be implemented separately).

Allow to add RPC method to AnyRpc server

It would be great to be able to add some custom rpc methods in AnyRpcServer.
However I did not find a solution without exposing anyrpc in the header of AnyRpcServer like this void addMethod(anyrpc::Method* method);

But by looking at the name of this class, does it really matter to hide anyrcp from AnyRpcServer ?

[Qt5] Project doesn't start when trying to instance QtQmlBot

I've have installed AnyRPC and Spix, and have added the lines specified in the readme into my .pro file;

...

# Spix
QT += quick
INCLUDEPATH += /usr/local/include
LIBS += -lSpix -lanyrpc

Building and running this by itself works fine, and doesn't produce any errors.
When I create a spix::AnyRpcServer instance with the default port, it also still works fine;

int main(int argc, char *argv[])
{
    ...

    QQmlApplicationEngine *engine = new QQmlApplicationEngine;
    engine->addImportPath("qrc:/");

     // Set up Spix server
    spix::AnyRpcServer server;
    // auto bot = new spix::QtQmlBot();
    // bot->runTestServer(server);
    
    ...
}

but as soon as I uncomment the auto bot = new spix::QtQmlBot(); line, I get the following errors in Qt Creator:

πŸ‘Ž error: /usr/local/lib/libSpix.a(QtItemTools.cpp.o): in function QByteArray::compare(char const*, Qt::CaseSensitivity) const': QtItemTools.cpp:-1: error: undefined reference to qstrnicmp(char const*, long long, char const*, long long)'
πŸ‘Ž error: collect2: error: ld returned 1 exit status
πŸ‘Ž error: [Makefile:82: mainapp] Error 1

And the following in the compile output


/usr/bin/ld: /usr/local/lib/libSpix.a(QtItemTools.cpp.o): in function `QByteArray::compare(char const*, Qt::CaseSensitivity) const':
QtItemTools.cpp:(.text._ZNK10QByteArray7compareEPKcN2Qt15CaseSensitivityE[_ZNK10QByteArray7compareEPKcN2Qt15CaseSensitivityE]+0x63): undefined reference to `qstrnicmp(char const*, long long, char const*, long long)'
collect2: error: ld returned 1 exit status
make[2]: *** [Makefile:82: mainapp] Error 1
make[2]: Leaving directory '/home/ruuds/Programming/djd/build-mea-Desktop_Qt_5_11_3_GCC_64bit-Debug/mainapp/mainapp'
make[1]: *** [Makefile:46: sub-mainapp-make_first] Error 2
make[1]: Leaving directory '/home/ruuds/Programming/djd/build-mea-Desktop_Qt_5_11_3_GCC_64bit-Debug/mainapp'
make: *** [Makefile:51: sub-mainapp-make_first] Error 2
15:59:58: The process "/usr/bin/make" exited with code 2.
Error while building/deploying project mea (kit: Desktop Qt 5.11.3 GCC 64bit)
When executing step "Make"

Any help would be appreciated!

Clicks on invisible objects

Hi @faaxm
In my Qt app I currently have buttons in a rowLayout, I use Spix in remote control with AnyRPC to control them and it works amazingly !

However, if one of these buttons turns invisible (button01), with its Qt-QML property visible: false, the layout automatically rearrange the other buttons (button00 and button02) putting them over the invisible button (button01). Then if I use another s.mouseClick("mainWindow/button01") Spix find well my button because it still exists, gets its bounding box and clicks there. Problem is it's invisible and button02 has overdisplay it, so button02 is triggered because there is no checks on the visible property before the click.

Of course it's easy to patch on the client side by checking with s.existsAndVisible("mainWindow/button01") but would it be convenient to add an anyRPC method in order to do that, for example a mouseClickWithCheck, or simply to have a ClickOnItem: [WARNING] item clicked but invisible like the Item not found report error in ClickOnItem::execute

I could propose such an implementation if you're interested in.
Thanks a lot πŸ˜‰

Questions about spix using

Hello,
I've got several questions during my work with spix:

  1. Does it possible to work out "Open file" and "Save file" dialogs in some way? I mean, the "FileDialog" QML type. For example, If it was possible to use keyboard without relation to any QML object, we would just wait for the dialog opens, type the required path (the path will be written to the corresponding line, because this line is in focus when the dialog appears) and click enter. As far as I'm concern, this is the easiest way.
  2. Relates to the previous point - as far as I understand, it's impossible now to use keyboard without connection to some object (the "inputText" function has two arguments - path to the object and text to type). Or, maybe, some way to use keyboard separately exists?
  3. I haven't understood assignments of some functions (at least comments are definitely required):
    a. Why do we need the access to the "start" function? As far as I understand, tests start by their own.
    b. "setCommandExecuter" and "setGenericCommandHandler" are some mysteries for me. Do I need to use them?
    c. I think, the "mouseDropUrls" function uses as an analog of text drag'n'droping to objects, am I right?
    d. What is "genericCommand" and how am I able to use it?
    e. "getStringProperty" and "setStringProperty" are not clear for me too.
    f. "getErrors" gets errors from where?

I'm sorry for bombarding you with questions, you've done a great job and I really appreciate it!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.