summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorRobert Griebl <robert.griebl@qt.io>2023-10-09 20:51:16 +0200
committerRobert Griebl <robert.griebl@qt.io>2023-11-08 16:19:36 +0100
commitfef2d9a8666e333b4e01518936de12b326d25478 (patch)
treed87e90d7f6ddfee83e729645bec8cb1ba5cdfa13 /tests
parent38ebc214dca4fd45560c91a89998378bb2492db9 (diff)
Add an auto-test for window focus/activation handling
Also added a getter for the actual "adapter" Quick item on the sys-ui side (backingItem), as it is needed for this test. It might come in handy for user projects as well, especially to get direct access to the QWaylandQuickItem. Change-Id: I9665ca31566e98bae086b8cdc1aa2df5cab4023e Reviewed-by: Bernd Weimer <bernd.weimer@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/qml/CMakeLists.txt1
-rw-r--r--tests/auto/qml/active/CMakeLists.txt6
-rw-r--r--tests/auto/qml/active/am-config.yaml7
-rw-r--r--tests/auto/qml/active/apps/app1/info.yaml8
-rw-r--r--tests/auto/qml/active/apps/app1/main.qml62
-rw-r--r--tests/auto/qml/active/apps/app2/info.yaml8
-rw-r--r--tests/auto/qml/active/apps/app2/main.qml62
-rw-r--r--tests/auto/qml/active/tst_active.qml176
8 files changed, 330 insertions, 0 deletions
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 3399e236..a246dda4 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -12,6 +12,7 @@ add_subdirectory(windowmanager)
add_subdirectory(windowmapping)
add_subdirectory(windowitem)
add_subdirectory(windowitem2)
+add_subdirectory(active)
add_subdirectory(installer)
add_subdirectory(quicklaunch)
add_subdirectory(intents)
diff --git a/tests/auto/qml/active/CMakeLists.txt b/tests/auto/qml/active/CMakeLists.txt
new file mode 100644
index 00000000..604f95d6
--- /dev/null
+++ b/tests/auto/qml/active/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+qt_am_internal_add_qml_test(tst_active
+ CONFIG_YAML am-config.yaml
+ EXTRA_FILES apps
+ TEST_FILE tst_active.qml
+)
diff --git a/tests/auto/qml/active/am-config.yaml b/tests/auto/qml/active/am-config.yaml
new file mode 100644
index 00000000..1aa8648a
--- /dev/null
+++ b/tests/auto/qml/active/am-config.yaml
@@ -0,0 +1,7 @@
+formatVersion: 1
+formatType: am-configuration
+---
+applications:
+ builtinAppsManifestDir: "${CONFIG_PWD}/apps"
+ installationDir: "/tmp/am/apps"
+ documentDir: "/tmp/am/docs"
diff --git a/tests/auto/qml/active/apps/app1/info.yaml b/tests/auto/qml/active/apps/app1/info.yaml
new file mode 100644
index 00000000..8614fb5e
--- /dev/null
+++ b/tests/auto/qml/active/apps/app1/info.yaml
@@ -0,0 +1,8 @@
+formatVersion: 1
+formatType: am-application
+---
+id: 'app1'
+code: 'main.qml'
+runtime: 'qml'
+intents:
+ - id: activeFocusItem
diff --git a/tests/auto/qml/active/apps/app1/main.qml b/tests/auto/qml/active/apps/app1/main.qml
new file mode 100644
index 00000000..73bbb861
--- /dev/null
+++ b/tests/auto/qml/active/apps/app1/main.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls.Basic
+import QtApplicationManager
+import QtApplicationManager.Application
+import QtTest
+
+
+ApplicationManagerWindow {
+ id: root
+ width: 120
+ height: 240
+ visible: true
+ title: "App 1"
+ color: active ? "green" : "grey"
+
+ Item {
+ id: content
+ objectName: "app1.content"
+ anchors.fill: parent
+ TextField {
+ id: textField
+ objectName: "app1.textField"
+ width: content.width
+ height: content.height / 3
+ text: "Text App 1"
+ focus: true
+ }
+ Button {
+ id: button
+ objectName: "app1.button"
+ y: textField.height
+ width: content.width
+ height: content.height / 3
+ text: "Btn App 1"
+ }
+ }
+
+ SignalSpy {
+ id: activeSpy
+ target: root
+ signalName: "activeChanged"
+ }
+
+ IntentHandler {
+ intentIds: [ "activeFocusItem" ]
+ onRequestReceived: (request) => {
+ const waitUntilActive = request.parameters["waitUntilActive"] || 0
+ const waitUntilInactive = request.parameters["waitUntilInactive"] || 0
+ if ((waitUntilActive && !root.active) || (waitUntilInactive && root.active)) {
+ activeSpy.clear()
+ activeSpy.wait(waitUntilActive || waitUntilInactive)
+ }
+
+ request.sendReply({ "active": root.active,
+ "amwindow": root.activeFocusItem?.objectName,
+ "qwindow": content.Window.activeFocusItem?.objectName })
+ }
+ }
+}
diff --git a/tests/auto/qml/active/apps/app2/info.yaml b/tests/auto/qml/active/apps/app2/info.yaml
new file mode 100644
index 00000000..68b0f5a0
--- /dev/null
+++ b/tests/auto/qml/active/apps/app2/info.yaml
@@ -0,0 +1,8 @@
+formatVersion: 1
+formatType: am-application
+---
+id: 'app2'
+code: 'main.qml'
+runtime: 'qml'
+intents:
+ - id: activeFocusItem
diff --git a/tests/auto/qml/active/apps/app2/main.qml b/tests/auto/qml/active/apps/app2/main.qml
new file mode 100644
index 00000000..8bf0585a
--- /dev/null
+++ b/tests/auto/qml/active/apps/app2/main.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls.Basic
+import QtApplicationManager
+import QtApplicationManager.Application
+import QtTest
+
+
+ApplicationManagerWindow {
+ id: root
+ width: 120
+ height: 240
+ visible: true
+ title: "App 2"
+ color: active ? "red" : "grey"
+
+ Item {
+ id: content
+ objectName: "app2.content"
+ anchors.fill: parent
+ TextField {
+ id: textField
+ objectName: "app2.textField"
+ width: content.width
+ height: content.height / 3
+ text: "Text App 2"
+ focus: true
+ }
+ Button {
+ id: button
+ objectName: "app2.button"
+ y: textField.height
+ width: content.width
+ height: content.height / 3
+ text: "Btn App 2"
+ }
+ }
+
+ SignalSpy {
+ id: activeSpy
+ target: root
+ signalName: "activeChanged"
+ }
+
+ IntentHandler {
+ intentIds: [ "activeFocusItem" ]
+ onRequestReceived: (request) => {
+ const waitUntilActive = request.parameters["waitUntilActive"] || 0
+ const waitUntilInactive = request.parameters["waitUntilInactive"] || 0
+ if ((waitUntilActive && !root.active) || (waitUntilInactive && root.active)) {
+ activeSpy.clear()
+ activeSpy.wait(waitUntilActive || waitUntilInactive)
+ }
+
+ request.sendReply({ "active": root.active,
+ "amwindow": root.activeFocusItem?.objectName,
+ "qwindow": content.Window.activeFocusItem?.objectName })
+ }
+ }
+}
diff --git a/tests/auto/qml/active/tst_active.qml b/tests/auto/qml/active/tst_active.qml
new file mode 100644
index 00000000..557c5144
--- /dev/null
+++ b/tests/auto/qml/active/tst_active.qml
@@ -0,0 +1,176 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQuick.Controls.Basic
+import QtTest
+import QtApplicationManager.SystemUI
+
+TestCase {
+ id: root
+ name: "Active"
+ when: windowShown
+ visible: true
+
+ width: 360
+ height: 240
+
+ FocusScope {
+ width: 120
+ height: 240
+ id: content
+ objectName: "sysui.content"
+ focus: false
+
+ TextField {
+ id: textField
+ objectName: "sysui.textField"
+ width: content.width
+ height: content.height / 3
+ text: "Text SysUI"
+ focus: true
+ }
+ Button {
+ id: button
+ objectName: "sysui.button"
+ y: textField.height
+ width: content.width
+ height: content.height / 3
+ text: "Btn SysUI"
+ }
+ }
+
+ WindowItem {
+ id: app1Win
+ x: 120
+ }
+ WindowItem {
+ id: app2Win
+ x: 240
+ }
+
+ Connections {
+ target: WindowManager
+
+ function onWindowAdded(window) {
+ if (window.application.id === "app1")
+ app1Win.window = window
+ else if (window.application.id === "app2")
+ app2Win.window = window
+ }
+
+ function onWindowContentStateChanged(window) {
+ if (window.contentState === WindowObject.NoSurface) {
+ if (window.application.id === "app1")
+ app1Win.window = null
+ else if (window.application.id === "app2")
+ app2Win.window = null
+ }
+ }
+ }
+
+ function test_focus()
+ {
+ if (root.Window.window.flags & Qt.WindowDoesNotAcceptFocus)
+ skip("Test can only be run without AM_BACKGROUND_TEST set, since it requires input focus");
+
+ // the starting focus is inconsistent between Linux and macOS/Windows, so we force it
+
+ content.forceActiveFocus()
+ verify(Window.activeFocusItem == textField)
+
+
+ // start app1, focus the window and check if app1's textField has focus now
+
+ ApplicationManager.startApplication("app1")
+ tryVerify(() => (app1Win.window?.application?.id === "app1"))
+
+ verify(Window.activeFocusItem == textField)
+ app1Win.forceActiveFocus()
+
+ let afi1Req = IntentClient.sendIntentRequest("activeFocusItem", "app1", { "waitUntilActive": 1000 })
+ tryCompare(afi1Req, "succeeded", true)
+ compare(afi1Req.result.active, true)
+ compare(afi1Req.result.amwindow, "app1.textField")
+ compare(afi1Req.result.qwindow, "app1.textField")
+
+ let afiBeforeApp2 = Window.activeFocusItem
+
+ if (ApplicationManager.singleProcess)
+ compare(Window.activeFocusItem?.objectName, "app1.textField")
+ else
+ compare(Window.activeFocusItem, app1Win.backingItem)
+
+
+ // start app2, focus the window and check if app2's textField has focus now
+
+ ApplicationManager.startApplication("app2")
+ tryVerify(() => (app2Win.window?.application?.id === "app2"))
+
+ verify(Window.activeFocusItem == afiBeforeApp2)
+ app2Win.forceActiveFocus()
+
+ let afi2Req = IntentClient.sendIntentRequest("activeFocusItem", "app2", { "waitUntilActive": 1000 })
+
+ tryCompare(afi2Req, "succeeded", true)
+ compare(afi2Req.result.active, true)
+ compare(afi2Req.result.amwindow, "app2.textField")
+ compare(afi2Req.result.qwindow, "app2.textField")
+
+ afi1Req = IntentClient.sendIntentRequest("activeFocusItem", "app1", { })
+ tryCompare(afi1Req, "succeeded", true)
+ compare(afi1Req.result.active, false)
+ compare(afi1Req.result.amwindow, undefined)
+
+ if (ApplicationManager.singleProcess)
+ compare(Window.activeFocusItem?.objectName, "app2.textField")
+ else
+ compare(Window.activeFocusItem, app2Win.backingItem)
+
+
+ // click into the empty area at the bottom of app1 to move the focus to app1
+
+ mouseClick(app1Win, 10, app1Win.height - 10)
+
+ afi1Req = IntentClient.sendIntentRequest("activeFocusItem", "app1", { "waitUntilActive": 1000 })
+
+ tryCompare(afi1Req, "succeeded", true)
+ compare(afi1Req.result.active, true)
+ compare(afi1Req.result.amwindow, "app1.textField")
+ compare(afi1Req.result.qwindow, "app1.textField")
+
+ afi2Req = IntentClient.sendIntentRequest("activeFocusItem", "app2", { })
+ tryCompare(afi2Req, "succeeded", true)
+ compare(afi2Req.result.active, false)
+
+ if (ApplicationManager.singleProcess)
+ compare(Window.activeFocusItem?.objectName, "app1.textField")
+ else
+ compare(Window.activeFocusItem, app1Win.backingItem)
+
+
+ // click into the empty area at the bottom of the sys-ui -> the focus should NOT move
+
+ mouseClick(content, 10, content.height - 10)
+ wait(100 * AmTest.timeoutFactor)
+
+ if (ApplicationManager.singleProcess)
+ compare(Window.activeFocusItem?.objectName, "app1.textField")
+ else
+ compare(Window.activeFocusItem, app1Win.backingItem)
+
+ // click into the sys-ui's textField to move the focus to the sys-ui
+
+ mouseClick(content, 10, 10)
+
+ compare(Window.activeFocusItem, textField)
+
+ afi1Req = IntentClient.sendIntentRequest("activeFocusItem", "app1", { "waitUntilInactive": 1000 })
+ tryCompare(afi1Req, "succeeded", true)
+ compare(afi1Req.result.active, false)
+
+ afi2Req = IntentClient.sendIntentRequest("activeFocusItem", "app2", { })
+ tryCompare(afi2Req, "succeeded", true)
+ compare(afi2Req.result.active, false)
+ }
+}