diff options
Diffstat (limited to 'tests/auto/controls')
-rw-r--r-- | tests/auto/controls/data/tst_abstractbutton.qml | 23 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_button.qml | 3 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_combobox.qml | 68 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_delaybutton.qml | 3 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_dial.qml | 22 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_spinbox.qml | 16 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_splitview.qml | 51 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_stackview.qml | 159 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_swipedelegate.qml | 3 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_tooltip.qml | 33 |
10 files changed, 366 insertions, 15 deletions
diff --git a/tests/auto/controls/data/tst_abstractbutton.qml b/tests/auto/controls/data/tst_abstractbutton.qml index ee26a6d6..da5642cc 100644 --- a/tests/auto/controls/data/tst_abstractbutton.qml +++ b/tests/auto/controls/data/tst_abstractbutton.qml @@ -887,4 +887,27 @@ TestCase { mouseRelease(control) compare(clickedSpy.count, 1) } + + function test_doubleClick() { + let control = createTemporaryObject(button, testCase, { text: "Hello" }) + verify(control) + + let pressedSpy = signalSpy.createObject(control, { target: control, signalName: "pressed" }) + verify(pressedSpy.valid) + + let releasedSpy = signalSpy.createObject(control, { target: control, signalName: "released" }) + verify(releasedSpy.valid) + + let clickedSpy = signalSpy.createObject(control, { target: control, signalName: "clicked" }) + verify(clickedSpy.valid) + + let doubleClickedSpy = signalSpy.createObject(control, { target: control, signalName: "doubleClicked" }) + verify(doubleClickedSpy.valid) + + mouseDoubleClickSequence(control) + compare(pressedSpy.count, 2) + compare(releasedSpy.count, 2) + compare(clickedSpy.count, 1) + compare(doubleClickedSpy.count, 1) + } } diff --git a/tests/auto/controls/data/tst_button.qml b/tests/auto/controls/data/tst_button.qml index bd4fe80e..83a6ea61 100644 --- a/tests/auto/controls/data/tst_button.qml +++ b/tests/auto/controls/data/tst_button.qml @@ -152,8 +152,7 @@ TestCase { "doubleClicked", ["pressedChanged", { "pressed": false }], ["downChanged", { "down": false }], - "released", - "clicked"] + "released"] mouseDoubleClickSequence(control, control.width / 2, control.height / 2, Qt.LeftButton) verify(sequenceSpy.success) } diff --git a/tests/auto/controls/data/tst_combobox.qml b/tests/auto/controls/data/tst_combobox.qml index 2cf71f73..2d5069b3 100644 --- a/tests/auto/controls/data/tst_combobox.qml +++ b/tests/auto/controls/data/tst_combobox.qml @@ -1853,4 +1853,72 @@ TestCase { closedSpy.wait() compare(closedSpy.count, 1) } + + function test_comboBoxSelectTextByMouse() { + let control = createTemporaryObject(comboBox, testCase, + { editable: true, selectTextByMouse: true, model: [ "Some text" ], width: parent.width }) + verify(control) + waitForRendering(control) + control.forceActiveFocus() + + // Position the text cursor at the beginning of the text. + mouseClick(control, control.leftPadding, control.height / 2) + // Select all of the text. + mousePress(control, control.leftPadding, control.height / 2) + mouseMove(control, control.leftPadding + control.contentItem.width, control.height / 2) + mouseRelease(control, control.leftPadding + control.contentItem.width, control.height / 2) + compare(control.contentItem.selectedText, "Some text") + } + + // QTBUG-78885: When the edit text is changed on an editable ComboBox, + // and then that ComboBox loses focus, its currentIndex should change + // to the index of the edit text (assuming a match is found). + function test_currentIndexChangeOnLostFocus() { + if (Qt.styleHints.tabFocusBehavior !== Qt.TabFocusAllControls) + skip("This platform only allows tab focus for text controls") + + let theModel = [] + for (let i = 0; i < 10; ++i) + theModel.push("Item " + (i + 1)) + + let comboBox1 = createTemporaryObject(comboBox, testCase, + { objectName: "comboBox1", editable: true, model: theModel }) + verify(comboBox1) + compare(comboBox1.currentIndex, 0) + + let comboBox2 = createTemporaryObject(comboBox, testCase, { objectName: "comboBox2" }) + verify(comboBox2) + + // Give the first ComboBox focus and type in 0 to select "Item 10" (default is "Item 1"). + waitForRendering(comboBox1) + comboBox1.forceActiveFocus() + verify(comboBox1.activeFocus) + keyClick(Qt.Key_0) + compare(comboBox1.editText, "Item 10") + + let currentIndexSpy = signalSpy.createObject(comboBox1, + { target: comboBox1, signalName: "currentIndexChanged" }) + verify(currentIndexSpy.valid) + + // Give focus to the other ComboBox so that the first one loses it. + // The first ComboBox's currentIndex should change to that of "Item 10". + keyClick(Qt.Key_Tab) + verify(comboBox2.activeFocus) + compare(comboBox1.currentIndex, 9) + compare(currentIndexSpy.count, 1) + + // Give focus back to the first ComboBox, and try the same thing except + // with non-existing text; the currentIndex should not change. + comboBox1.forceActiveFocus() + verify(comboBox1.activeFocus) + keySequence(StandardKey.SelectAll) + compare(comboBox1.contentItem.selectedText, "Item 10") + keyClick(Qt.Key_N) + keyClick(Qt.Key_O) + keyClick(Qt.Key_P) + keyClick(Qt.Key_E) + compare(comboBox1.editText, "nope") + compare(comboBox1.currentIndex, 9) + compare(currentIndexSpy.count, 1) + } } diff --git a/tests/auto/controls/data/tst_delaybutton.qml b/tests/auto/controls/data/tst_delaybutton.qml index 2560177d..e965b5ef 100644 --- a/tests/auto/controls/data/tst_delaybutton.qml +++ b/tests/auto/controls/data/tst_delaybutton.qml @@ -173,8 +173,7 @@ TestCase { "doubleClicked", ["pressedChanged", { "pressed": false }], ["downChanged", { "down": false }], - "released", - "clicked"] + "released"] mouseDoubleClickSequence(control, control.width / 2, control.height / 2, Qt.LeftButton) verify(sequenceSpy.success) } diff --git a/tests/auto/controls/data/tst_dial.qml b/tests/auto/controls/data/tst_dial.qml index 86999594..26f30c33 100644 --- a/tests/auto/controls/data/tst_dial.qml +++ b/tests/auto/controls/data/tst_dial.qml @@ -223,33 +223,39 @@ TestCase { var minimumExpectedValueCount = data.live ? 2 : 1; // drag to the left - mouseDrag(dial, dial.width / 2, dial.height / 2, -dial.width / 2, 0, Qt.LeftButton); + // we always add or subtract 1 to ensure we start the drag from the opposite side + // of where we're dragging to, for more reliable tests + mouseDrag(dial, dial.width / 2 + 1, dial.height / 2, -dial.width / 2, 0, Qt.LeftButton); fuzzyCompare(dial.value, data.leftValue, 0.1); - verify(valueSpy.count >= minimumExpectedValueCount); + verify(valueSpy.count >= minimumExpectedValueCount, "expected valueChanged to be emitted at least " + + minimumExpectedValueCount + " time(s), but it was only emitted " + valueSpy.count + " time(s)"); valueSpy.clear(); verify(moveSpy.count > 0); moveSpy.clear(); // drag to the top - mouseDrag(dial, dial.width / 2, dial.height / 2, 0, -dial.height / 2, Qt.LeftButton); + mouseDrag(dial, dial.width / 2, dial.height / 2 + 1, 0, -dial.height / 2, Qt.LeftButton); fuzzyCompare(dial.value, data.topValue, 0.1); - verify(valueSpy.count >= minimumExpectedValueCount); + verify(valueSpy.count >= minimumExpectedValueCount, "expected valueChanged to be emitted at least " + + minimumExpectedValueCount + " time(s), but it was only emitted " + valueSpy.count + " time(s)"); valueSpy.clear(); verify(moveSpy.count > 0); moveSpy.clear(); // drag to the right - mouseDrag(dial, dial.width / 2, dial.height / 2, dial.width / 2, 0, Qt.LeftButton); + mouseDrag(dial, dial.width / 2 - 1, dial.height / 2, dial.width / 2, 0, Qt.LeftButton); fuzzyCompare(dial.value, data.rightValue, 0.1); - verify(valueSpy.count >= minimumExpectedValueCount); + verify(valueSpy.count >= minimumExpectedValueCount, "expected valueChanged to be emitted at least " + + minimumExpectedValueCount + " time(s), but it was only emitted " + valueSpy.count + " time(s)"); valueSpy.clear(); verify(moveSpy.count > 0); moveSpy.clear(); // drag to the bottom (* 0.6 to ensure we don't go over to the minimum position) - mouseDrag(dial, dial.width / 2, dial.height / 2, 10, dial.height / 2, Qt.LeftButton); + mouseDrag(dial, dial.width / 2, dial.height / 2 - 1, 10, dial.height / 2, Qt.LeftButton); fuzzyCompare(dial.value, data.bottomValue, 0.1); - verify(valueSpy.count >= minimumExpectedValueCount); + verify(valueSpy.count >= minimumExpectedValueCount, "expected valueChanged to be emitted at least " + + minimumExpectedValueCount + " time(s), but it was only emitted " + valueSpy.count + " time(s)"); valueSpy.clear(); verify(moveSpy.count > 0); moveSpy.clear(); diff --git a/tests/auto/controls/data/tst_spinbox.qml b/tests/auto/controls/data/tst_spinbox.qml index d3a0d8bb..22dbb352 100644 --- a/tests/auto/controls/data/tst_spinbox.qml +++ b/tests/auto/controls/data/tst_spinbox.qml @@ -658,4 +658,20 @@ TestCase { compare(control.displayText, data.displayTexts[i]) } } + + Component { + id: overriddenSpinBox + SpinBox { + value: 50 + up.indicator: Rectangle { + property string s: "this is the one" + } + } + } + + function test_indicatorOverridden() { + var control = createTemporaryObject(overriddenSpinBox, testCase) + verify(control) + compare(control.up.indicator.s, "this is the one"); + } } diff --git a/tests/auto/controls/data/tst_splitview.qml b/tests/auto/controls/data/tst_splitview.qml index c125b99e..74e4c68e 100644 --- a/tests/auto/controls/data/tst_splitview.qml +++ b/tests/auto/controls/data/tst_splitview.qml @@ -2026,4 +2026,55 @@ TestCase { compare(control.repeater.count, 3) compare(control.contentChildren.length, 3) } + + Component { + id: hoverableChildrenSplitViewComponent + + SplitView { + handle: handleComponent + anchors.fill: parent + + MouseArea { + objectName: "mouseArea1" + hoverEnabled: true + + SplitView.preferredWidth: 200 + } + MouseArea { + objectName: "mouseArea2" + hoverEnabled: true + } + } + } + + function test_hoverableChilden() { + if (Qt.platform.pluginName === "offscreen" || Qt.platform.pluginName === "minimal") + skip("Mouse hovering not functional on offscreen/minimal platforms") + + var control = createTemporaryObject(hoverableChildrenSplitViewComponent, testCase) + verify(control) + + verify(isPolishScheduled(control)) + verify(waitForItemPolished(control)) + + // Move the mouse over the handle. + var handles = findHandles(control) + var targetHandle = handles[0] + // Test fails if we don't do two moves for some reason... + mouseMove(targetHandle, targetHandle.width / 2, targetHandle.height / 2) + mouseMove(targetHandle, targetHandle.width / 2, targetHandle.height / 2) + verify(targetHandle.SplitHandle.hovered) + + // Move the mouse to the MouseArea on the left. The handle should no longer be hovered. + mouseMove(control, 100, control.height / 2) + verify(!targetHandle.SplitHandle.hovered) + + // Move the mouse back over the handle. + mouseMove(targetHandle, targetHandle.width / 2, targetHandle.height / 2) + verify(targetHandle.SplitHandle.hovered) + + // Move the mouse to the MouseArea on the right. The handle should no longer be hovered. + mouseMove(control, control.width - 100, control.height / 2) + verify(!targetHandle.SplitHandle.hovered) + } } diff --git a/tests/auto/controls/data/tst_stackview.qml b/tests/auto/controls/data/tst_stackview.qml index a9fbf874..c15ce8ea 100644 --- a/tests/auto/controls/data/tst_stackview.qml +++ b/tests/auto/controls/data/tst_stackview.qml @@ -1245,4 +1245,163 @@ TestCase { gc() verify(control.initialItem) } + + // Need to use this specific structure in order to reproduce the crash. + Component { + id: clearUponDestructionContainerComponent + + Item { + id: container + objectName: "container" + + property alias control: stackView + property var onDestructionCallback + + property Component clearUponDestructionComponent: Component { + id: clearUponDestructionComponent + + Item { + objectName: "clearUponDestructionItem" + Component.onDestruction: container.onDestructionCallback(stackView) + } + } + + StackView { + id: stackView + initialItem: Item { + objectName: "initialItem" + } + } + } + } + + // QTBUG-80353 + // Tests that calling clear() in Component.onDestruction in response to that + // item being removed (e.g. via an earlier call to clear()) results in a warning and not a crash. + function test_recursiveClearClear() { + let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase, + { onDestructionCallback: function(stackView) { stackView.clear(StackView.Immediate) }}) + verify(container) + + let control = container.control + control.push(container.clearUponDestructionComponent, StackView.Immediate) + + // Shouldn't crash. + ignoreWarning(new RegExp(".*cannot clear while already in the process of removing elements")) + control.clear(StackView.Immediate) + } + + function test_recursivePopClear() { + let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase, + { onDestructionCallback: function(stackView) { stackView.clear(StackView.Immediate) }}) + verify(container) + + let control = container.control + control.push(container.clearUponDestructionComponent, StackView.Immediate) + + // Pop all items except the first, removing the second item we pushed in the process. + // Shouldn't crash. + ignoreWarning(new RegExp(".*cannot clear while already in the process of removing elements")) + control.pop(null, StackView.Immediate) + } + + function test_recursivePopPop() { + let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase, + { onDestructionCallback: function(stackView) { stackView.pop(null, StackView.Immediate) }}) + verify(container) + + let control = container.control + // Push an extra item so that we can call pop(null) and reproduce the conditions for the crash. + control.push(component, StackView.Immediate) + control.push(container.clearUponDestructionComponent, StackView.Immediate) + + // Pop the top item, then pop down to the first item in response. + ignoreWarning(new RegExp(".*cannot pop while already in the process of removing elements")) + control.pop(StackView.Immediate) + } + + function test_recursiveReplaceClear() { + let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase, + { onDestructionCallback: function(stackView) { stackView.clear(StackView.Immediate) }}) + verify(container) + + let control = container.control + control.push(container.clearUponDestructionComponent, StackView.Immediate) + + // Replace the top item, then clear in response. + ignoreWarning(new RegExp(".*cannot clear while already in the process of removing elements")) + control.replace(component, StackView.Immediate) + } + + function test_recursiveClearReplace() { + let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase, + { onDestructionCallback: function(stackView) { stackView.replace(component, StackView.Immediate) }}) + verify(container) + + let control = container.control + control.push(container.clearUponDestructionComponent, StackView.Immediate) + + // Replace the top item, then clear in response. + ignoreWarning(new RegExp(".*cannot replace while already in the process of removing elements")) + control.clear(StackView.Immediate) + } + + Component { + id: rectangleComponent + Rectangle {} + } + + Component { + id: qtbug57267_StackViewComponent + + StackView { + id: stackView + + popEnter: Transition { + XAnimator { from: (stackView.mirrored ? -1 : 1) * -stackView.width; to: 0; duration: 400; easing.type: Easing.Linear } + } + popExit: Transition { + XAnimator { from: 0; to: (stackView.mirrored ? -1 : 1) * stackView.width; duration: 400; easing.type: Easing.Linear } + } + pushEnter: Transition { + XAnimator { from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: 400; easing.type: Easing.Linear } + } + pushExit: Transition { + XAnimator { from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: 400; easing.type: Easing.Linear } + } + replaceEnter: Transition { + XAnimator { from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: 400; easing.type: Easing.Linear } + } + replaceExit: Transition { + XAnimator { from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: 400; easing.type: Easing.Linear } + } + } + } + + function test_qtbug57267() { + let redRect = createTemporaryObject(rectangleComponent, testCase, { color: "red" }) + verify(redRect) + let blueRect = createTemporaryObject(rectangleComponent, testCase, { color: "blue" }) + verify(blueRect) + let control = createTemporaryObject(qtbug57267_StackViewComponent, testCase, + { "anchors.fill": testCase, initialItem: redRect }) + verify(control) + + control.replace(blueRect) + compare(control.currentItem, blueRect) + compare(control.depth, 1) + + // Wait until the animation has started and then interrupt it by pushing the redRect. + tryCompare(control, "busy", true) + control.replace(redRect) + // The blue rect shouldn't be visible since we replaced it and therefore interrupted its animation. + tryCompare(blueRect, "visible", false) + // We did the replace very early on, so the transition for the redRect should still be happening. + compare(control.busy, true) + compare(redRect.visible, true) + + // After finishing the transition, the red rect should still be visible. + tryCompare(control, "busy", false) + compare(redRect.visible, true) + } } diff --git a/tests/auto/controls/data/tst_swipedelegate.qml b/tests/auto/controls/data/tst_swipedelegate.qml index 3e2ff460..71eb0b99 100644 --- a/tests/auto/controls/data/tst_swipedelegate.qml +++ b/tests/auto/controls/data/tst_swipedelegate.qml @@ -654,8 +654,7 @@ TestCase { "pressed", "doubleClicked", ["pressedChanged", { "pressed": false }], - "released", - "clicked" + "released" ]; mouseDoubleClickSequence(control, control.width / 2, control.height / 2, Qt.LeftButton); verify(mouseSignalSequenceSpy.success); diff --git a/tests/auto/controls/data/tst_tooltip.qml b/tests/auto/controls/data/tst_tooltip.qml index d98a7cc8..6d45b09d 100644 --- a/tests/auto/controls/data/tst_tooltip.qml +++ b/tests/auto/controls/data/tst_tooltip.qml @@ -227,15 +227,46 @@ TestCase { id: toolTipWithExitTransition ToolTip { + Component.onCompleted: contentItem.objectName = "contentItem" + enter: Transition { NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 100 } } exit: Transition { - NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 1000 } + NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 500 } } } } + function test_openDuringExitTransitionWithTimeout() { + let control = createTemporaryObject(toolTipWithExitTransition, testCase, { timeout: 250 }) + verify(control) + + let openedSpy = signalSpy.createObject(control, { target: control, signalName: "opened" }) + verify(openedSpy.valid) + + control.open() + verify(control.visible) + // Can't be fully open yet because the enter transition has only just started. + compare(control.opened, false) + compare(control.enter.running, true) + // Wait for it to have opened. We don't check that the opened property is still true + // because it can result in hard-to-reproduce flakiness. Instead we just check that + // it was opened at some point. + tryCompare(openedSpy, "count", 1) + + // Let it timeout and begin the exit transition. + tryCompare(control, "opened", false) + verify(control.visible) + tryCompare(control.exit, "running", true) + verify(control.visible) + + // Quickly open it again; it should still timeout eventually. + control.open() + tryCompare(openedSpy, "count", 2) + tryCompare(control.exit, "running", true) + } + function test_makeVisibleWhileExitTransitionRunning_data() { return [ { tag: "imperative", imperative: true }, |