diff options
author | Robert Griebl <robert.griebl@qt.io> | 2023-10-09 20:51:16 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@qt.io> | 2023-11-08 16:19:36 +0100 |
commit | fef2d9a8666e333b4e01518936de12b326d25478 (patch) | |
tree | d87e90d7f6ddfee83e729645bec8cb1ba5cdfa13 /tests | |
parent | 38ebc214dca4fd45560c91a89998378bb2492db9 (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.txt | 1 | ||||
-rw-r--r-- | tests/auto/qml/active/CMakeLists.txt | 6 | ||||
-rw-r--r-- | tests/auto/qml/active/am-config.yaml | 7 | ||||
-rw-r--r-- | tests/auto/qml/active/apps/app1/info.yaml | 8 | ||||
-rw-r--r-- | tests/auto/qml/active/apps/app1/main.qml | 62 | ||||
-rw-r--r-- | tests/auto/qml/active/apps/app2/info.yaml | 8 | ||||
-rw-r--r-- | tests/auto/qml/active/apps/app2/main.qml | 62 | ||||
-rw-r--r-- | tests/auto/qml/active/tst_active.qml | 176 |
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) + } +} |