on xenial help sign in
Add a qmluitest file for tutorial
commit9dd6ce492f734a577ea8569b4daed2c9a67f3031 authorMichael Terry <michael.terry@canonical.com> dateTue 28 Oct 2014, 11:15 (12 years ago) parent8cebdc7
6 files changed+321−145
qml/Shell.qml+5−2 qml/Tutorial/Tutorial.qml+17−3 qml/Tutorial/TutorialPage.qml+11−16 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Components/tst_EdgeDemoOverlay.qml−123 tests/qmltests/Tutorial/tst_Tutorial.qml+287
qml/Shell.qml+5−2
@@ -211,6 +211,9 @@ Item {
211211 lockscreen.hide() // show locked app
212212 }
213213 launcher.hide();
214+ if (appId != "unity8-dash") {
215+ edgeDemo.finish(); // accepting a call makes the tutorial go away
216+ }
214217 }
215218 }
216219
@@ -759,7 +762,7 @@ Item {
759762
760763 Tutorial {
761764 id: edgeDemo
762− objectName: "edgeDemo"
765+ objectName: "tutorial"
763766 visible: AccountsService.demoEdges
764767 paused: Powerd.status === Powerd.Off || LightDM.Greeter.active
765768 launcher: launcher
@@ -776,7 +779,7 @@ Item {
776779
777780 Rectangle {
778781 id: shutdownFadeOutRectangle
779− z: edgeDemo.z + 10
782+ z: alphaDisclaimerLabel.z + 10
780783 enabled: false
781784 visible: false
782785 color: "black"
qml/Tutorial/Tutorial.qml+17−3
@@ -24,18 +24,27 @@ Item {
2424 property Item indicators
2525 property Item stages
2626
27− readonly property bool launcherEnabled: loader.sourceComponent === leftComponent ||
27+ readonly property bool launcherEnabled: !running ||
28+ loader.sourceComponent === leftComponent ||
2829 loader.sourceComponent === leftFinishComponent
2930 readonly property bool stagesEnabled: !running
30− readonly property bool panelEnabled: loader.sourceComponent === topComponent ||
31+ readonly property bool panelEnabled: !running ||
32+ loader.sourceComponent === topComponent ||
3133 loader.sourceComponent === topFinishComponent
3234 readonly property bool panelContentEnabled: !running
3335 readonly property bool running: loader.sourceComponent !== null
3436
3537 property bool paused: false
3638
39+ property int readingDelay: 3500
40+
3741 signal finished()
3842
43+ function finish() {
44+ d.stop();
45+ finished();
46+ }
47+
3948 ////
4049
4150 visible: false
@@ -150,12 +159,13 @@ Item {
150159 Component {
151160 id: introComponent
152161 TutorialIntro {
162+ objectName: "tutorialIntro"
153163 parent: root.stages
154164 anchors.fill: parent
155165 backgroundFadesOut: false
156166
157167 Timer {
158− interval: 3500
168+ interval: root.readingDelay
159169 running: !root.paused
160170 onTriggered: loader.load(topComponent)
161171 }
@@ -165,6 +175,7 @@ Item {
165175 Component {
166176 id: topComponent
167177 TutorialTop {
178+ objectName: "tutorialTop"
168179 parent: root.stages
169180 anchors.fill: parent
170181 anchors.topMargin: root.indicators.panelHeight
@@ -176,6 +187,7 @@ Item {
176187 Component {
177188 id: topFinishComponent
178189 TutorialTopFinish {
190+ objectName: "tutorialTopFinish"
179191 parent: root.indicators
180192 anchors.bottom: parent ? parent.content.bottom : undefined
181193 anchors.left: parent ? parent.content.left : undefined
@@ -187,6 +199,7 @@ Item {
187199 Component {
188200 id: leftComponent
189201 TutorialLeft {
202+ objectName: "tutorialLeft"
190203 parent: root.stages
191204 anchors.fill: parent
192205 pageNumber: 2
@@ -197,6 +210,7 @@ Item {
197210 Component {
198211 id: leftFinishComponent
199212 TutorialLeftFinish {
213+ objectName: "tutorialLeftFinish"
200214 parent: root.stages
201215 anchors.fill: parent
202216 textXOffset: root.launcher.panelWidth
qml/Tutorial/TutorialPage.qml+11−16
@@ -19,10 +19,7 @@ import Ubuntu.Components 1.1
1919import "../Components"
2020
2121Showable {
22− id: overlay
23−
24− // Valid values are "left", "right", "top", "bottom", or "none"
25− property string edge: "none"
22+ id: root
2623
2724 // This is the header displayed, like "Right edge"
2825 property alias title: titleLabel.text
@@ -33,13 +30,10 @@ Showable {
3330 // This is the text for the skip button
3431 property string skipText: i18n.tr("Skip tutorial")
3532
36− // Whether this demo is running currently
37− readonly property bool active: available && visible
38−
3933 // Whether animations are paused
4034 property bool paused
4135
42− // Which page number this overlay represents, 1-indexed
36+ // Which page number this page represents, 1-indexed
4337 property int pageNumber
4438
4539 // How many pages there are total
@@ -51,7 +45,7 @@ Showable {
5145 // Whether whole page (background + foreground) or just the foreground fades out
5246 property bool backgroundFadesOut: true
5347
54− // An integrated glowing Bar class
48+ // An integrated glowing Bar class, for convenience
5549 property alias bar: bar
5650
5751 // The foreground Item, add children to it that you want to fade in
@@ -83,7 +77,7 @@ Showable {
8377 duration: UbuntuAnimation.BriskDuration
8478 onRunningChanged: {
8579 if (!running) {
86− overlay.finished();
80+ root.finished();
8781 }
8882 }
8983 }
@@ -169,7 +163,7 @@ Showable {
169163 }
170164
171165 Row {
172− visible: overlay.pageNumber > 0
166+ visible: root.pageNumber > 0
173167 spacing: units.gu(0.5)
174168 anchors {
175169 bottom: parent.bottom
@@ -177,11 +171,11 @@ Showable {
177171 horizontalCenter: parent.horizontalCenter
178172 }
179173 Repeater {
180− model: overlay.pageTotal
174+ model: root.pageTotal
181175 Image {
182176 height: units.gu(1)
183177 width: height
184− source: (index === overlay.pageNumber - 1) ?
178+ source: (index === root.pageNumber - 1) ?
185179 "../Dash/graphics/pagination_dot_on.png" :
186180 "../Dash/graphics/pagination_dot_off.png"
187181 }
@@ -189,7 +183,7 @@ Showable {
189183 }
190184
191185 MouseArea {
192− visible: overlay.pageNumber > 0
186+ visible: root.pageNumber > 0
193187 enabled: visible
194188 anchors {
195189 bottom: parent.bottom
@@ -200,12 +194,13 @@ Showable {
200194
201195 Label {
202196 id: skipLabel
197+ objectName: "skipLabel"
203198 anchors.centerIn: parent
204199 // Translators: This is the arrow for "Skip tutorial" buttons
205− text: i18n.tr("%1 〉").arg(overlay.skipText)
200+ text: i18n.tr("%1 〉").arg(root.skipText)
206201 }
207202
208− onClicked: overlay.hide()
203+ onClicked: root.hide()
209204 }
210205
211206 Bar {
tests/qmltests/CMakeLists.txt+1−1
@@ -25,7 +25,6 @@ add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/test
2525add_qml_test(Components Background)
2626add_qml_test(Components Carousel)
2727add_qml_test(Components DraggingArea)
28−add_qml_test(Components EdgeDemoOverlay)
2928add_qml_test(Components LazyImage)
3029add_qml_test(Components Lockscreen)
3130add_qml_test(Components Rating)
@@ -89,3 +88,4 @@ add_qml_test(Stages SpreadDelegate ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_D
8988add_qml_test(Stages SurfaceContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
9089add_qml_test(Stages SessionContainer ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
9190add_qml_test(Stages Splash)
91+add_qml_test(Tutorial Tutorial ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin")
tests/qmltests/Components/tst_EdgeDemoOverlay.qmldeleted−123
@@ -1,123 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import "../../../qml/Components"
21−
22−Item {
23− id: root
24− width: boxWidth * 3
25− height: boxHeight * 2
26−
27− property int boxWidth: 250
28− property int boxHeight: 250
29−
30− EdgeDemoOverlay {
31− id: top
32− edge: "top"
33− title: "Top"
34− text: "Displayed on top left"
35− anchors.left: parent.left
36− anchors.top: parent.top
37− width: boxWidth
38− height: boxHeight
39− }
40−
41− EdgeDemoOverlay {
42− id: right
43− edge: "right"
44− title: "Right"
45− text: "Displayed on top right"
46− anchors.right: parent.right
47− anchors.top: parent.top
48− width: boxWidth
49− height: boxHeight
50− }
51−
52− EdgeDemoOverlay {
53− id: left
54− edge: "left"
55− title: "Left"
56− text: "Displayed on bottom right"
57− anchors.right: parent.right
58− anchors.bottom: parent.bottom
59− width: boxWidth
60− height: boxHeight
61− available: false
62− }
63−
64− EdgeDemoOverlay {
65− id: bottom
66− edge: "bottom"
67− title: "Bottom"
68− text: "Displayed on bottom left"
69− anchors.left: parent.left
70− anchors.bottom: parent.bottom
71− width: boxWidth
72− height: boxHeight
73− }
74−
75− EdgeDemoOverlay {
76− id: none
77− edge: "none"
78− title: "None"
79− text: "Displayed on top middle"
80− anchors.horizontalCenter: parent.horizontalCenter
81− anchors.top: parent.top
82− width: boxWidth
83− height: boxHeight
84− }
85−
86− SignalSpy {
87− id: signalSpy
88− }
89−
90− UT.UnityTestCase {
91− name: "EdgeDemoOverlay"
92− when: windowShown
93−
94− function test_animations() {
95− compare(right.running, true)
96−
97− compare(left.running, false)
98− left.available = true
99− compare(left.running, true)
100− }
101−
102− function test_skip() {
103− signalSpy.target = bottom
104− signalSpy.signalName = "skip"
105− signalSpy.clear()
106− var bottomSkip = findChild(bottom, "skipLabel")
107− mousePress(bottomSkip, 1, 1)
108− mouseRelease(bottomSkip, 1, 1)
109− signalSpy.wait()
110− compare(bottom.available, false)
111−
112− // Test that the 'none' edge skips anywhere
113− signalSpy.target = none
114− signalSpy.clear()
115− var backgroundShade = findChild(none, "backgroundShadeMouseArea")
116− tryCompare(backgroundShade, "enabled", true)
117− mousePress(backgroundShade, 1, 1)
118− mouseRelease(backgroundShade, 1, 1)
119− signalSpy.wait()
120− compare(none.available, false)
121− }
122− }
123−}
tests/qmltests/Tutorial/tst_Tutorial.qmlnew file+287
@@ -0,0 +1,287 @@
1+/*
2+ * Copyright (C) 2013,2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import AccountsService 0.1
20+import LightDM 0.1 as LightDM
21+import Ubuntu.Components 1.1
22+import Unity.Application 0.1
23+import Unity.Test 0.1 as UT
24+
25+import "../../../qml"
26+
27+Item {
28+ id: root
29+ width: shellLoader.width + buttons.width
30+ height: shellLoader.height
31+
32+ QtObject {
33+ id: applicationArguments
34+
35+ function hasGeometry() {
36+ return false;
37+ }
38+
39+ function width() {
40+ return 0;
41+ }
42+
43+ function height() {
44+ return 0;
45+ }
46+ }
47+
48+ Row {
49+ spacing: 0
50+ anchors.fill: parent
51+
52+ Loader {
53+ id: shellLoader
54+
55+ width: units.gu(40)
56+ height: units.gu(71)
57+
58+ property bool itemDestroyed: false
59+ sourceComponent: Component {
60+ Shell {
61+ property string indicatorProfile: "phone"
62+
63+ Component.onDestruction: {
64+ shellLoader.itemDestroyed = true;
65+ }
66+ }
67+ }
68+ }
69+
70+ Rectangle {
71+ id: buttons
72+ color: "white"
73+ width: units.gu(30)
74+ height: shellLoader.height
75+
76+ Column {
77+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
78+ spacing: units.gu(1)
79+ Row {
80+ anchors { left: parent.left; right: parent.right }
81+ Button {
82+ text: "Restart Tutorial"
83+ onClicked: {
84+ if (shellLoader.status !== Loader.Ready)
85+ return;
86+
87+ AccountsService.demoEdges = false;
88+ AccountsService.demoEdges = true;
89+ }
90+ }
91+ }
92+ }
93+ }
94+ }
95+
96+ UT.UnityTestCase {
97+ id: testCase
98+ name: "Tutorial"
99+ when: windowShown
100+
101+ property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
102+ property real halfWidth: shell ? shell.width / 2 : 0
103+ property real halfHeight: shell ? shell.height / 2 : 0
104+
105+ function init() {
106+ swipeAwayGreeter();
107+ AccountsService.demoEdges = false;
108+ AccountsService.demoEdges = true;
109+
110+ var tutorial = findChild(shell, "tutorial");
111+ tutorial.readingDelay = 1;
112+ }
113+
114+ function cleanup() {
115+ shellLoader.itemDestroyed = false;
116+
117+ shellLoader.active = false;
118+
119+ tryCompare(shellLoader, "status", Loader.Null);
120+ tryCompare(shellLoader, "item", null);
121+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
122+ // item might still be alive. So if we set Loader.active back to true
123+ // again right now we will get the very same Shell instance back. So no reload
124+ // actually took place. Likely because Loader waits until the next event loop
125+ // iteration to do its work. So to ensure the reload, we will wait until the
126+ // Shell instance gets destroyed.
127+ tryCompare(shellLoader, "itemDestroyed", true);
128+
129+ // kill all (fake) running apps
130+ killApps();
131+
132+ // reload our test subject to get it in a fresh state once again
133+ shellLoader.active = true;
134+
135+ tryCompare(shellLoader, "status", Loader.Ready);
136+ removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
137+ }
138+
139+ function killApps() {
140+ while (ApplicationManager.count > 1) {
141+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
142+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
143+ }
144+ compare(ApplicationManager.count, 1)
145+ }
146+
147+ function swipeAwayGreeter() {
148+ var greeter = findChild(shell, "greeter");
149+ tryCompare(greeter, "showProgress", 1);
150+
151+ touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
152+
153+ // wait until the animation has finished
154+ tryCompare(greeter, "showProgress", 0);
155+ waitForRendering(greeter);
156+ }
157+
158+ function waitForPage(name) {
159+ tryCompareFunction(function() { return findChild(shell, name) !== null; }, true);
160+ return findChild(shell, name);
161+ }
162+
163+ function checkTopEdge() {
164+ touchFlick(shell, halfWidth, 0, halfWidth, halfHeight);
165+
166+ var panel = findChild(shell, "panel");
167+ tryCompare(panel.indicators, "fullyClosed", true);
168+ }
169+
170+ function checkLeftEdge() {
171+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
172+
173+ var launcher = findChild(shell, "launcher");
174+ tryCompare(launcher, "state", "");
175+ }
176+
177+ function checkRightEdge() {
178+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
179+
180+ var stage = findChild(shell, "stage");
181+ var spreadView = findChild(stage, "spreadView");
182+ tryCompare(spreadView, "phase", 0);
183+ }
184+
185+ function checkBottomEdge() {
186+ // Can't actually check effect of swipe, since dash isn't really loaded
187+ var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader");
188+ tryCompare(applicationsDisplayLoader.item, "interactive", false);
189+ }
190+
191+ function checkFinished() {
192+ tryCompare(AccountsService, "demoEdges", false);
193+
194+ var tutorial = findChild(shell, "tutorial");
195+ tryCompare(tutorial, "running", false);
196+
197+ var launcher = findChild(shell, "launcher");
198+ tryCompare(launcher, "shown", false);
199+ }
200+
201+ function goToPage(name) {
202+ var page = waitForPage("tutorialIntro");
203+ if (name === "tutorialIntro") return page;
204+ // Intro page will pass by itself
205+
206+ page = waitForPage("tutorialTop");
207+ checkLeftEdge();
208+ checkRightEdge();
209+ checkBottomEdge();
210+ if (name === "tutorialTop") return page;
211+ touchFlick(shell, halfWidth, 0, halfWidth, shell.height);
212+
213+ page = waitForPage("tutorialTopFinish");
214+ checkLeftEdge();
215+ checkRightEdge();
216+ checkBottomEdge();
217+ if (name === "tutorialTopFinish") return page;
218+ touchFlick(shell, halfWidth, shell.height, halfWidth, 0);
219+
220+ page = waitForPage("tutorialLeft");
221+ checkTopEdge();
222+ checkRightEdge();
223+ checkBottomEdge();
224+ if (name === "tutorialLeft") return page;
225+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight, true, false);
226+
227+ page = waitForPage("tutorialLeftFinish");
228+ if (name === "tutorialLeftFinish") return page;
229+ touchFlick(shell, halfWidth, halfHeight, halfWidth, halfHeight, false, true);
230+
231+ checkFinished();
232+ return null;
233+ }
234+
235+ function test_skippable() {
236+ var top = goToPage("tutorialTop");
237+
238+ var skipLabel = findChild(top, "skipLabel");
239+ tap(skipLabel);
240+
241+ checkFinished();
242+ }
243+
244+ function test_walkthrough() {
245+ goToPage(null);
246+ }
247+
248+ function test_launcherShortDrags() {
249+ // goToPage does a full launcher pull. But here we want to test
250+ // just barely pulling the launcher out and letting go (i.e. not
251+ // triggering the "progress" property of Launcher).
252+
253+ var left = goToPage("tutorialLeft");
254+
255+ // Make sure we don't do anything if we don't pull the launcher
256+ // out much.
257+ var launcher = findChild(shell, "launcher");
258+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.6, halfHeight);
259+ tryCompare(launcher, "state", ""); // should remain hidden
260+ tryCompare(left, "shown", true); // and we should still be on left
261+
262+ // Now drag out but not past launcher itself
263+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
264+
265+ // That actually finishes the launcher tutorial (drag is already
266+ // over, it would be silly to show LeftFinish page that asks for a
267+ // drag release)
268+ checkFinished();
269+ }
270+
271+ function test_launcherDragBack() {
272+ // goToPage does a full launcher pull. But here we test pulling
273+ // all the way out, then dragging back into place.
274+
275+ goToPage("tutorialLeftFinish");
276+ touchFlick(shell, halfWidth, halfHeight, 0, halfHeight, false, true);
277+
278+ checkFinished();
279+ }
280+
281+ function test_interrupted() {
282+ goToPage("tutorialTop");
283+ ApplicationManager.startApplication("dialer-app");
284+ checkFinished();
285+ }
286+ }
287+}