Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add qmlbot #476

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 70 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions .idea/pytest-qt.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ repos:
rev: 1.13.0
hooks:
- id: blacken-docs
additional_dependencies: [black==20.8b1]
additional_dependencies: [black>=22.1.0]
language_version: python3
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
Expand Down
33 changes: 33 additions & 0 deletions docs/qmlbot.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
=========
qmlbot
=========

Fixture that helps interacting with QML.
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved

Example - load qml from string:
.. code-block:: python

def test_say_hello(qmlbot):
qml = """
import QtQuick 2.0

Rectangle{
objectName: "sample";
property string hello: "world"
}
"""
item = qmlbot.loads(qml)
assert item.property("hello") == "world"

Example - load qml from file

.. code-block:: python
from pathlib import Path


def test_say_hello(qmlbot):
item = qmlbot.load(Path("sayhello.qml"))
assert item.property("hello") == "world"

Note: if your components depends on any instances or ``@QmlElement``'s you need
to make sure it is acknowledge by ``qmlbot.engine``
6 changes: 6 additions & 0 deletions src/pytestqt/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
_QtExceptionCaptureManager,
)
from pytestqt.logging import QtLoggingPlugin, _QtMessageCapture
from pytestqt.qml.qmlbot import QmlBot
from pytestqt.qt_compat import qt_api
from pytestqt.qtbot import QtBot, _close_widgets

Expand Down Expand Up @@ -93,6 +94,11 @@ def qtbot(qapp, request):
return result


@pytest.fixture
def qmlbot(qapp):
nicoddemus marked this conversation as resolved.
Show resolved Hide resolved
return QmlBot()


@pytest.fixture
def qtlog(request):
"""Fixture that can access messages captured during testing"""
Expand Down
Empty file added src/pytestqt/qml/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions src/pytestqt/qml/botloader.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import QtQuick 2.15
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
import QtQuick.Window 2.15

Window {
id: root
width: 500
height: 400
visible: true


Item {
anchors.fill: parent
Loader {
objectName: "contentloader"
source: ""
}
}
}
41 changes: 41 additions & 0 deletions src/pytestqt/qml/qmlbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from pathlib import Path

from pytestqt.qt_compat import qt_api


class QmlBot:
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
def __init__(self):
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
self.engine = qt_api.QtQml.QQmlApplicationEngine()
main = Path(__file__).parent / "botloader.qml"
self.engine.load(main.resolve(True))

@property
def _loader(self):
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
self.root = self.engine.rootObjects()[0]
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
return self.root.findChild(qt_api.QtQuick.QQuickItem, "contentloader")

def load(self, path: Path):
"""
:returns: `QQuickItem` - the initialized component
"""
self._loader.setProperty("source", str(path.resolve(True)))
return self._loader.property("item")

def loads(self, content: str):
"""
:returns: `QQuickItem` - the initialized component
"""
self.comp = qt_api.QtQml.QQmlComponent(
self.engine
) # needed for it not to be collected by the gc
self.comp.setData(content.encode("utf-8"), qt_api.QtCore.QUrl())
if self.comp.status() != qt_api.QtQml.QQmlComponent.Status.Ready:
raise RuntimeError(
f"component {self.comp} is not Ready:\n"
f"STATUS: {self.comp.status()}\n"
f"HINT: make sure there are no wrong spaces.\n"
f"ERRORS: {self.comp.errors()}"
)
self._loader.setProperty("source", "")
self._loader.setProperty("sourceComponent", self.comp)
return self._loader.property("item")
2 changes: 2 additions & 0 deletions src/pytestqt/qt_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def _import_module(module_name):
self.QtGui = _import_module("QtGui")
self.QtTest = _import_module("QtTest")
self.QtWidgets = _import_module("QtWidgets")
self.QtQml = _import_module("QtQml")
self.QtQuick = _import_module("QtQuick")

self._check_qt_api_version()

Expand Down
Empty file added tests/test_qml/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions tests/test_qml/sample.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import QtQuick 2.0

Rectangle{
objectName: "sample";
property string hello: "world"
}
31 changes: 31 additions & 0 deletions tests/test_qml/test_qmlbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from pathlib import Path

import pytest


def test_load_from_string_wrong_syntax(qmlbot):
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
nicoddemus marked this conversation as resolved.
Show resolved Hide resolved
qml = "import QtQuick 2.0 Rectangle{"
with pytest.raises(RuntimeError):
qmlbot.loads(qml)


def test_load_from_string(qmlbot):
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
text = "that's a template!"
qml = (
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
"""
import QtQuick 2.0

Rectangle{
objectName: "sample";
property string hello: "%s"
}
"""
% text
)
item = qmlbot.loads(qml)
assert item.property("hello") == text


def test_load_from_file(qmlbot):
nrbnlulu marked this conversation as resolved.
Show resolved Hide resolved
item = qmlbot.load(Path(__file__).parent / "sample.qml")
assert item.property("hello") == "world"