diff options
Diffstat (limited to 'tests/auto/controls/data/tst_tumbler.qml')
-rw-r--r-- | tests/auto/controls/data/tst_tumbler.qml | 472 |
1 files changed, 387 insertions, 85 deletions
diff --git a/tests/auto/controls/data/tst_tumbler.qml b/tests/auto/controls/data/tst_tumbler.qml index 2e0e3295..01d5cdee 100644 --- a/tests/auto/controls/data/tst_tumbler.qml +++ b/tests/auto/controls/data/tst_tumbler.qml @@ -40,7 +40,7 @@ import QtQuick 2.2 import QtTest 1.0 -import QtQuick.Controls 2.0 +import QtQuick.Controls 2.1 TestCase { id: testCase @@ -58,6 +58,16 @@ TestCase { readonly property real implicitTumblerHeight: 200 readonly property real defaultImplicitDelegateHeight: implicitTumblerHeight / 3 readonly property real defaultListViewTumblerOffset: -defaultImplicitDelegateHeight + readonly property real tumblerDelegateHeight: tumbler ? tumbler.availableHeight / tumbler.visibleItemCount : 0 + property Item tumblerView: null + + Component { + id: tumblerComponent + + Tumbler { + visibleItemCount: 3 + } + } Component { id: itemComponent @@ -73,16 +83,20 @@ TestCase { } function cleanup() { - var destroyed = false; - cleanupItem.Component.destruction.connect(function() { destroyed = true; }); - cleanupItem.destroy(); - // Waiting until it's deleted before continuing makes debugging // test failures much easier, because there aren't unrelated items hanging around. - // TODO: Replace with tryVerify(!tumbler) in 5.8. - while (!destroyed) - wait(0) + tryVerify(function() { return !tumbler; }); + } + + function createTumbler(args) { + if (args === undefined) + tumbler = tumblerComponent.createObject(cleanupItem); + else + tumbler = tumblerComponent.createObject(cleanupItem, args); + verify(tumbler, "Tumbler: failed to create an instance"); + tumblerView = findView(tumbler); + verify(tumblerView); } function tumblerXCenter() { @@ -95,26 +109,46 @@ TestCase { // visualItemIndex is from 0 to the amount of visible items. function itemCenterPos(visualItemIndex) { - var halfDelegateHeight = tumbler.contentItem.delegateHeight / 2; + var halfDelegateHeight = tumblerDelegateHeight / 2; var yCenter = tumbler.y + tumbler.topPadding + halfDelegateHeight - + (tumbler.contentItem.delegateHeight * visualItemIndex); + + (tumblerDelegateHeight * visualItemIndex); return Qt.point(tumblerXCenter(), yCenter); } function checkItemSizes() { - var contentChildren = tumbler.contentItem.hasOwnProperty("contentItem") - ? tumbler.contentItem.contentItem.children : tumbler.contentItem.children; + var contentChildren = tumbler.wrap ? tumblerView.children : tumblerView.contentItem.children; verify(contentChildren.length >= tumbler.count); for (var i = 0; i < contentChildren.length; ++i) { - compare(contentChildren[i].width, tumbler.width); - compare(contentChildren[i].height, tumbler.contentItem.delegateHeight); + compare(contentChildren[i].width, tumbler.availableWidth); + compare(contentChildren[i].height, tumblerDelegateHeight); } } - Component { - id: tumblerComponent + function findView(parent) { + for (var i = 0; i < parent.children.length; ++i) { + var child = parent.children[i]; + if (child.hasOwnProperty("currentIndex")) { + return child; + } + + return findView(child); + } - Tumbler {} + return null; + } + + property Component noAttachedPropertiesDelegate: Text { + text: modelData + } + + function test_wrapWithoutAttachedProperties() { + createTumbler(); + verify(tumbler.wrap); + + tumbler.delegate = noAttachedPropertiesDelegate; + // Shouldn't assert. + tumbler.wrap = false; + verify(findView(tumbler)); } // TODO: test that currentIndex is maintained between contentItem changes... @@ -122,8 +156,7 @@ TestCase { // } function test_currentIndex() { - tumbler = tumblerComponent.createObject(cleanupItem); - verify(tumbler); + createTumbler(); compare(tumbler.contentItem.parent, tumbler); tumbler.model = 5; @@ -133,75 +166,193 @@ TestCase { // Set it through user interaction. var pos = Qt.point(tumblerXCenter(), tumbler.height / 2); - mouseDrag(tumbler, pos.x, pos.y, 0, -tumbler.contentItem.delegateHeight / 2, Qt.LeftButton, Qt.NoModifier, 200); - compare(tumbler.currentIndex, 1); - compare(tumbler.contentItem.currentIndex, 1); + mouseDrag(tumbler, pos.x, pos.y, 0, tumbler.height / 3, Qt.LeftButton, Qt.NoModifier, 200); + tryCompare(tumblerView, "offset", 1); + compare(tumbler.currentIndex, 4); + compare(tumblerView.currentIndex, 4); // Set it manually. tumbler.currentIndex = 2; tryCompare(tumbler, "currentIndex", 2); - compare(tumbler.contentItem.currentIndex, 2); + compare(tumblerView.currentIndex, 2); - // PathView has 0 as its currentIndex in this case for some reason. tumbler.model = null; - tryCompare(tumbler, "currentIndex", 0); + tryCompare(tumbler, "currentIndex", -1); + // PathView will only use 0 as the currentIndex when there are no items. + compare(tumblerView.currentIndex, 0); tumbler.model = ["A", "B", "C"]; tryCompare(tumbler, "currentIndex", 0); + + // Setting a negative current index should have no effect, because the model isn't empty. + tumbler.currentIndex = -1; + compare(tumbler.currentIndex, 0); + + tumbler.model = 1; + compare(tumbler.currentIndex, 0); + + tumbler.model = 5; + compare(tumbler.count, 5); + tumblerView = findView(tumbler); + tryCompare(tumblerView, "count", 5); + tumbler.currentIndex = 4; + compare(tumbler.currentIndex, 4); + compare(tumblerView.currentIndex, 4); + + --tumbler.model; + compare(tumbler.count, 4); + compare(tumblerView.count, 4); + // Removing an item from an integer-based model will cause views to reset their currentIndex to 0. + compare(tumbler.currentIndex, 0); + compare(tumblerView.currentIndex, 0); + + tumbler.model = 0; + compare(tumbler.currentIndex, -1); } - function test_keyboardNavigation() { - tumbler = tumblerComponent.createObject(cleanupItem); + Component { + id: currentIndexTumbler + + Tumbler { + model: 5 + currentIndex: 2 + visibleItemCount: 3 + } + } + + Component { + id: currentIndexTumblerNoWrap + + Tumbler { + model: 5 + currentIndex: 2 + wrap: false + visibleItemCount: 3 + } + } + + Component { + id: currentIndexTumblerNoWrapReversedOrder + + Tumbler { + model: 5 + wrap: false + currentIndex: 2 + visibleItemCount: 3 + } + } + + Component { + id: negativeCurrentIndexTumblerNoWrap + + Tumbler { + model: 5 + wrap: false + currentIndex: -1 + visibleItemCount: 3 + } + } + + Component { + id: currentIndexTooLargeTumbler + + Tumbler { + objectName: "currentIndexTooLargeTumbler" + model: 10 + currentIndex: 10 + } + } + + + function test_currentIndexAtCreation_data() { + return [ + { tag: "wrap: implicit, expected currentIndex: 2", currentIndex: 2, wrap: true, component: currentIndexTumbler }, + { tag: "wrap: false, expected currentIndex: 2", currentIndex: 2, wrap: false, component: currentIndexTumblerNoWrap }, + // Order of property assignments shouldn't matter + { tag: "wrap: false, expected currentIndex: 2, reversed property assignment order", + currentIndex: 2, wrap: false, component: currentIndexTumblerNoWrapReversedOrder }, + { tag: "wrap: false, expected currentIndex: 0", currentIndex: 0, wrap: false, component: negativeCurrentIndexTumblerNoWrap }, + { tag: "wrap: implicit, expected currentIndex: 0", currentIndex: 0, wrap: true, component: currentIndexTooLargeTumbler } + ] + } + + function test_currentIndexAtCreation(data) { + // Test setting currentIndex at creation time + tumbler = data.component.createObject(cleanupItem); verify(tumbler); + // A "statically declared" currentIndex will be pending until the count has changed, + // which happens when the model is set, which happens on the TumblerView's next polish. + tryCompare(tumbler, "currentIndex", data.currentIndex); + + tumblerView = findView(tumbler); + // TODO: replace once QTBUG-19708 is fixed. + for (var delay = 1000; delay >= 0; delay -= 50) { + if (tumblerView.currentItem) + break; + wait(50); + } + verify(tumblerView.currentItem); + compare(tumblerView.currentIndex, data.currentIndex); + compare(tumblerView.currentItem.text, data.currentIndex.toString()); + + var fuzz = 1; + if (data.wrap) { + fuzzyCompare(tumblerView.offset, data.currentIndex > 0 ? tumblerView.count - data.currentIndex : 0, fuzz); + } else { + fuzzyCompare(tumblerView.contentY, tumblerDelegateHeight * data.currentIndex - tumblerView.preferredHighlightBegin, fuzz); + } + } + + function test_keyboardNavigation() { + createTumbler(); tumbler.model = 5; tumbler.forceActiveFocus(); - tumbler.contentItem.highlightMoveDuration = 0; + tumblerView.highlightMoveDuration = 0; // Navigate upwards through entire wheel. for (var j = 0; j < tumbler.count - 1; ++j) { keyClick(Qt.Key_Up, Qt.NoModifier); - tryCompare(tumbler.contentItem, "offset", j + 1); + tryCompare(tumblerView, "offset", j + 1); compare(tumbler.currentIndex, tumbler.count - 1 - j); } keyClick(Qt.Key_Up, Qt.NoModifier); - tryCompare(tumbler.contentItem, "offset", 0); + tryCompare(tumblerView, "offset", 0); compare(tumbler.currentIndex, 0); // Navigate downwards through entire wheel. for (j = 0; j < tumbler.count - 1; ++j) { keyClick(Qt.Key_Down, Qt.NoModifier); - tryCompare(tumbler.contentItem, "offset", tumbler.count - 1 - j); + tryCompare(tumblerView, "offset", tumbler.count - 1 - j); compare(tumbler.currentIndex, j + 1); } keyClick(Qt.Key_Down, Qt.NoModifier); - tryCompare(tumbler.contentItem, "offset", 0); + tryCompare(tumblerView, "offset", 0); compare(tumbler.currentIndex, 0); } function test_itemsCorrectlyPositioned() { - tumbler = tumblerComponent.createObject(cleanupItem); - verify(tumbler); + createTumbler(); tumbler.model = 4; tumbler.height = 120; - compare(tumbler.contentItem.delegateHeight, 40); + compare(tumblerDelegateHeight, 40); checkItemSizes(); - wait(tumbler.contentItem.highlightMoveDuration); + wait(tumblerView.highlightMoveDuration); var firstItemCenterPos = itemCenterPos(1); - var firstItem = tumbler.contentItem.itemAt(firstItemCenterPos.x, firstItemCenterPos.y); + var firstItem = tumblerView.itemAt(firstItemCenterPos.x, firstItemCenterPos.y); var actualPos = cleanupItem.mapFromItem(firstItem, 0, 0); compare(actualPos.x, tumbler.leftPadding); compare(actualPos.y, tumbler.topPadding + 40); tumbler.forceActiveFocus(); keyClick(Qt.Key_Down); - tryCompare(tumbler.contentItem, "offset", 3.0); + tryCompare(tumblerView, "offset", 3.0); firstItemCenterPos = itemCenterPos(0); - firstItem = tumbler.contentItem.itemAt(firstItemCenterPos.x, firstItemCenterPos.y); + firstItem = tumblerView.itemAt(firstItemCenterPos.x, firstItemCenterPos.y); verify(firstItem); // Test QTBUG-40298. actualPos = cleanupItem.mapFromItem(firstItem, 0, 0); @@ -209,12 +360,12 @@ TestCase { compare(actualPos.y, tumbler.topPadding); var secondItemCenterPos = itemCenterPos(1); - var secondItem = tumbler.contentItem.itemAt(secondItemCenterPos.x, secondItemCenterPos.y); + var secondItem = tumblerView.itemAt(secondItemCenterPos.x, secondItemCenterPos.y); verify(secondItem); verify(firstItem.y < secondItem.y); var thirdItemCenterPos = itemCenterPos(2); - var thirdItem = tumbler.contentItem.itemAt(thirdItemCenterPos.x, thirdItemCenterPos.y); + var thirdItem = tumblerView.itemAt(thirdItemCenterPos.x, thirdItemCenterPos.y); verify(thirdItem); verify(firstItem.y < thirdItem.y); verify(secondItem.y < thirdItem.y); @@ -241,18 +392,18 @@ TestCase { tumbler = component.createObject(cleanupItem); // Should not be any warnings. - compare(tumbler.dayTumbler.currentIndex, 0); + tryCompare(tumbler.dayTumbler, "currentIndex", 0); compare(tumbler.dayTumbler.count, 31); compare(tumbler.monthTumbler.currentIndex, 0); compare(tumbler.monthTumbler.count, 12); compare(tumbler.yearTumbler.currentIndex, 0); compare(tumbler.yearTumbler.count, 100); - verify(tumbler.dayTumbler.contentItem.children.length >= tumbler.dayTumbler.visibleItemCount); - verify(tumbler.monthTumbler.contentItem.children.length >= tumbler.monthTumbler.visibleItemCount); + verify(findView(tumbler.dayTumbler).children.length >= tumbler.dayTumbler.visibleItemCount); + verify(findView(tumbler.monthTumbler).children.length >= tumbler.monthTumbler.visibleItemCount); // TODO: do this properly somehow wait(100); - verify(tumbler.yearTumbler.contentItem.children.length >= tumbler.yearTumbler.visibleItemCount); + verify(findView(tumbler.yearTumbler).children.length >= tumbler.yearTumbler.visibleItemCount); // March. tumbler.monthTumbler.currentIndex = 2; @@ -361,27 +512,106 @@ TestCase { } function test_displacement(data) { - tumbler = tumblerComponent.createObject(cleanupItem); - verify(tumbler); + createTumbler(); // TODO: test setting these in the opposite order (delegate after model // doesn't seem to cause a change in delegates in PathView) + tumbler.wrap = true; tumbler.delegate = displacementDelegate; tumbler.model = data.count; compare(tumbler.count, data.count); - var delegate = findChild(tumbler.contentItem, "delegate" + data.index); + var delegate = findChild(tumblerView, "delegate" + data.index); verify(delegate); - tumbler.contentItem.offset = data.offset; + tumblerView.offset = data.offset; compare(delegate.displacement, data.expectedDisplacement); // test displacement after adding and removing items } + function test_wrap() { + createTumbler(); + + tumbler.model = 5; + compare(tumbler.count, 5); + + tumbler.currentIndex = 2; + compare(tumblerView.currentIndex, 2); + + tumbler.wrap = false; + tumblerView = findView(tumbler); + compare(tumbler.count, 5); + compare(tumbler.currentIndex, 2); + // Tumbler's count hasn't changed (the model hasn't changed), + // but the new view needs time to instantiate its items. + tryCompare(tumblerView, "count", 5); + compare(tumblerView.currentIndex, 2); + } + + Component { + id: twoItemTumbler + + Tumbler { + model: 2 + } + } + + Component { + id: tenItemTumbler + + Tumbler { + model: 10 + } + } + + function test_countWrap() { + tumbler = tumblerComponent.createObject(cleanupItem); + verify(tumbler); + + // Check that a count that is less than visibleItemCount results in wrap being set to false. + verify(2 < tumbler.visibleItemCount); + tumbler.model = 2; + compare(tumbler.count, 2); + compare(tumbler.wrap, false); + } + + function test_explicitlyNonwrapping() { + // Check that explicitly setting wrap to false works even when it was implicitly false. + var explicitlyNonWrapping = twoItemTumbler.createObject(cleanupItem); + verify(explicitlyNonWrapping); + tryCompare(explicitlyNonWrapping, "wrap", false); + + explicitlyNonWrapping.wrap = false; + // wrap shouldn't be set to true now that there are more items than there are visible ones. + verify(10 > explicitlyNonWrapping.visibleItemCount); + explicitlyNonWrapping.model = 10; + compare(explicitlyNonWrapping.wrap, false); + + // Test resetting wrap back to the default behavior. + explicitlyNonWrapping.wrap = undefined; + compare(explicitlyNonWrapping.wrap, true); + } + + function test_explicitlyWrapping() { + // Check that explicitly setting wrap to true works even when it was implicitly true. + var explicitlyWrapping = tenItemTumbler.createObject(cleanupItem); + verify(explicitlyWrapping); + compare(explicitlyWrapping.wrap, true); + + explicitlyWrapping.wrap = true; + // wrap shouldn't be set to false now that there are more items than there are visible ones. + explicitlyWrapping.model = 2; + compare(explicitlyWrapping.wrap, true); + + // Test resetting wrap back to the default behavior. + explicitlyWrapping.wrap = undefined; + compare(explicitlyWrapping.wrap, false); + } + Component { - id: listViewTumblerComponent - //! [contentItem] + id: customListViewTumblerComponent + Tumbler { id: listViewTumbler @@ -397,7 +627,95 @@ TestCase { clip: true } } - //! [contentItem] + } + + Component { + id: customPathViewTumblerComponent + + Tumbler { + id: pathViewTumbler + + contentItem: PathView { + id: pathView + model: pathViewTumbler.model + delegate: pathViewTumbler.delegate + clip: true + pathItemCount: pathViewTumbler.visibleItemCount + 1 + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + dragMargin: width / 2 + + path: Path { + startX: pathView.width / 2 + startY: -pathView.delegateHeight / 2 + PathLine { + x: pathView.width / 2 + y: pathView.pathItemCount * pathView.delegateHeight - pathView.delegateHeight / 2 + } + } + + property real delegateHeight: pathViewTumbler.availableHeight / pathViewTumbler.visibleItemCount + } + } + } + + function test_customContentItemAtConstruction_data() { + return [ + { tag: "ListView", component: customListViewTumblerComponent }, + { tag: "PathView", component: customPathViewTumblerComponent } + ]; + } + + function test_customContentItemAtConstruction(data) { + var tumbler = data.component.createObject(cleanupItem); + // Shouldn't assert. + + tumbler.model = 5; + compare(tumbler.count, 5); + + tumbler.currentIndex = 2; + var tumblerView = findView(tumbler); + compare(tumblerView.currentIndex, 2); + + tumblerView.incrementCurrentIndex(); + compare(tumblerView.currentIndex, 3); + compare(tumbler.currentIndex, 3); + + // Shouldn't have any affect. + tumbler.wrap = false; + compare(tumbler.count, 5); + compare(tumblerView.currentIndex, 3); + compare(tumbler.currentIndex, 3); + } + + function test_customContentItemAfterConstruction_data() { + return [ + { tag: "ListView", componentPath: "TumblerListView.qml" }, + { tag: "PathView", componentPath: "TumblerPathView.qml" } + ]; + } + + function test_customContentItemAfterConstruction(data) { + createTumbler(); + + tumbler.model = 5; + compare(tumbler.count, 5); + + tumbler.currentIndex = 2; + compare(tumblerView.currentIndex, 2); + + var contentItemComponent = Qt.createComponent(data.componentPath); + compare(contentItemComponent.status, Component.Ready); + + var customContentItem = contentItemComponent.createObject(tumbler); + tumbler.contentItem = customContentItem; + compare(tumbler.count, 5); + tumblerView = findView(tumbler); + compare(tumblerView.currentIndex, 2); + + tumblerView.incrementCurrentIndex(); + compare(tumblerView.currentIndex, 3); + compare(tumbler.currentIndex, 3); } function test_displacementListView_data() { @@ -438,20 +756,18 @@ TestCase { } function test_displacementListView(data) { - // Sanity check that they're aren't any children at this stage. - tryCompare(cleanupItem.children, "length", 0); - - tumbler = listViewTumblerComponent.createObject(cleanupItem); - verify(tumbler); + createTumbler(); + tumbler.wrap = false; tumbler.delegate = displacementDelegate; tumbler.model = 5; compare(tumbler.count, 5); // Ensure assumptions about the tumbler used in our data() function are correct. - compare(tumbler.contentItem.contentY, -defaultImplicitDelegateHeight); + tumblerView = findView(tumbler); + compare(tumblerView.contentY, -defaultImplicitDelegateHeight); var delegateCount = 0; - var listView = tumbler.contentItem; - var listViewContentItem = tumbler.contentItem.contentItem; + var listView = tumblerView; + var listViewContentItem = tumblerView.contentItem; // We use the mouse instead of setting contentY directly, otherwise the // items snap back into place. This doesn't seem to be an issue for @@ -511,16 +827,17 @@ TestCase { } function test_listViewFlickAboveBounds(data) { - tumbler = listViewTumblerComponent.createObject(cleanupItem); - verify(tumbler); + createTumbler(); + tumbler.wrap = false; tumbler.delegate = displacementDelegate; tumbler.model = data.model; + tumblerView = findView(tumbler); mousePress(tumbler, tumblerXCenter(), tumblerYCenter()); // Ensure it's stationary. - var listView = tumbler.contentItem; + var listView = tumblerView; compare(listView.contentY, defaultListViewTumblerOffset); // We could just move up until the contentY changed, but this is safer. @@ -577,8 +894,7 @@ TestCase { } function test_visibleItemCount(data) { - tumbler = tumblerComponent.createObject(cleanupItem); - verify(tumbler); + createTumbler(); tumbler.delegate = objectNameDelegate; tumbler.visibleItemCount = data.visibleItemCount; @@ -588,9 +904,9 @@ TestCase { for (var delegateIndex = 0; delegateIndex < data.visibleItemCount; ++delegateIndex) { if (data.expectedYPositions.hasOwnProperty(delegateIndex)) { - var delegate = findChild(tumbler.contentItem, "delegate" + delegateIndex); + var delegate = findChild(tumblerView, "delegate" + delegateIndex); verify(delegate, "Delegate found at index " + delegateIndex); - var expectedYPos = data.expectedYPositions[delegateIndex] * tumbler.contentItem.delegateHeight; + var expectedYPos = data.expectedYPositions[delegateIndex] * tumblerDelegateHeight; compare(delegate.mapToItem(tumbler.contentItem, 0, 0).y, expectedYPos); } } @@ -604,12 +920,6 @@ TestCase { property real displacement: Tumbler.displacement } - property Component gridViewComponent: GridView {} - property Component simpleDisplacementDelegate: Text { - property real displacement: Tumbler.displacement - property int index: -1 - } - function test_attachedProperties() { tumbler = tumblerComponent.createObject(cleanupItem); verify(tumbler); @@ -621,19 +931,12 @@ TestCase { // // Cause displacement to be changed. The warning isn't triggered if we don't do this. // tumbler.contentItem.offset += 1; - ignoreWarning("Tumbler: attached properties must be accessed from within a delegate item that has a parent"); + ignoreWarning("Tumbler: attached properties must be accessed through a delegate item that has a parent"); noParentDelegateComponent.createObject(null); ignoreWarning("Tumbler: attempting to access attached property on item without an \"index\" property"); var object = noParentDelegateComponent.createObject(cleanupItem); verify(object); - object.destroy(); - - // Should not be any warnings from this, as ListView, for example, doesn't produce warnings for the same code. - var gridView = gridViewComponent.createObject(cleanupItem); - object = simpleDisplacementDelegate.createObject(gridView); - verify(object); - object.destroy(); } property Component paddingDelegate: Text { @@ -680,8 +983,7 @@ TestCase { } function test_padding(data) { - tumbler = tumblerComponent.createObject(cleanupItem); - verify(tumbler); + createTumbler(); tumbler.delegate = paddingDelegate; tumbler.model = 5; @@ -733,8 +1035,8 @@ TestCase { } // Force new items to be created, as there was a bug where the path was correct until this happened. - compare(tumbler.contentItem.offset, 0); + compare(tumblerView.offset, 0); ++tumbler.currentIndex; - tryCompare(tumbler.contentItem, "offset", 4, tumbler.contentItem.highlightMoveDuration * 2); + tryCompare(tumblerView, "offset", 4, tumblerView.highlightMoveDuration * 2); } } |