@@ -0,0 +1,341 @@
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+ tryCompare(shell, "enabled", true); // enabled by greeter when ready
107+ AccountsService.demoEdges = false;
108+ AccountsService.demoEdges = true;
109+ swipeAwayGreeter();
110+ }
111+
112+ function cleanup() {
113+ shellLoader.itemDestroyed = false;
114+
115+ shellLoader.active = false;
116+
117+ tryCompare(shellLoader, "status", Loader.Null);
118+ tryCompare(shellLoader, "item", null);
119+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
120+ // item might still be alive. So if we set Loader.active back to true
121+ // again right now we will get the very same Shell instance back. So no reload
122+ // actually took place. Likely because Loader waits until the next event loop
123+ // iteration to do its work. So to ensure the reload, we will wait until the
124+ // Shell instance gets destroyed.
125+ tryCompare(shellLoader, "itemDestroyed", true);
126+
127+ // kill all (fake) running apps
128+ killApps();
129+
130+ // reload our test subject to get it in a fresh state once again
131+ shellLoader.active = true;
132+
133+ tryCompare(shellLoader, "status", Loader.Ready);
134+ removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
135+ }
136+
137+ function killApps() {
138+ while (ApplicationManager.count > 1) {
139+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
140+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
141+ }
142+ compare(ApplicationManager.count, 1)
143+ }
144+
145+ function swipeAwayGreeter() {
146+ var coverPage = findChild(shell, "coverPage");
147+ tryCompare(coverPage, "showProgress", 1);
148+
149+ touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
150+
151+ // wait until the animation has finished
152+ var greeter = findChild(shell, "greeter");
153+ tryCompare(greeter, "required", false);
154+ waitForRendering(greeter);
155+ }
156+
157+ function waitForPage(name) {
158+ waitForRendering(findChild(shell, name));
159+ var page = findChild(shell, name);
160+ tryCompare(page, "shown", true);
161+ tryCompare(page.showAnimation, "running", false);
162+ return page;
163+ }
164+
165+ function checkTopEdge() {
166+ touchFlick(shell, halfWidth, 0, halfWidth, halfHeight);
167+
168+ var panel = findChild(shell, "panel");
169+ tryCompare(panel.indicators, "fullyClosed", true);
170+ }
171+
172+ function checkLeftEdge() {
173+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
174+
175+ var launcher = findChild(shell, "launcher");
176+ tryCompare(launcher, "state", "");
177+ }
178+
179+ function checkRightEdge() {
180+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
181+
182+ var stage = findChild(shell, "stage");
183+ var spreadView = findChild(stage, "spreadView");
184+ tryCompare(spreadView, "phase", 0);
185+ }
186+
187+ function checkBottomEdge() {
188+ // Can't actually check effect of swipe, since dash isn't really loaded
189+ var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader");
190+ tryCompare(applicationsDisplayLoader.item, "interactive", false);
191+ }
192+
193+ function checkFinished() {
194+ tryCompare(AccountsService, "demoEdges", false);
195+
196+ var tutorial = findChild(shell, "tutorial");
197+ tryCompare(tutorial, "running", false);
198+
199+ var launcher = findChild(shell, "launcher");
200+ tryCompare(launcher, "shown", false);
201+ }
202+
203+ function goToPage(name) {
204+ var page = waitForPage("tutorialLeft");
205+ checkTopEdge();
206+ checkRightEdge();
207+ checkBottomEdge();
208+ if (name === "tutorialLeft") return page;
209+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
210+
211+ page = waitForPage("tutorialLeftFinish");
212+ if (name === "tutorialLeftFinish") return page;
213+ var tick = findChild(page, "tick");
214+ tap(tick);
215+
216+ page = waitForPage("tutorialRight");
217+ checkTopEdge();
218+ checkLeftEdge();
219+ checkBottomEdge();
220+ if (name === "tutorialRight") return page;
221+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
222+ var overlay = findChild(page, "overlay");
223+ tryCompare(overlay, "shown", true);
224+ var tick = findChild(page, "tick");
225+ tap(tick);
226+
227+ var page = waitForPage("tutorialBottom");
228+ checkTopEdge();
229+ checkLeftEdge();
230+ checkRightEdge();
231+ if (name === "tutorialBottom") return page;
232+ touchFlick(shell, halfWidth, shell.height, halfWidth, halfHeight);
233+
234+ var page = waitForPage("tutorialBottomFinish");
235+ checkTopEdge();
236+ checkLeftEdge();
237+ checkRightEdge();
238+ checkBottomEdge();
239+ if (name === "tutorialBottomFinish") return page;
240+ var tick = findChild(page, "tick");
241+ tap(tick);
242+
243+ checkFinished();
244+ return null;
245+ }
246+
247+ function test_walkthrough() {
248+ goToPage(null);
249+ }
250+
251+ function test_launcherShortDrag() {
252+ // goToPage does a normal launcher pull. But here we want to test
253+ // just barely pulling the launcher out and letting go (i.e. not
254+ // triggering the "progress" property of Launcher).
255+
256+ var left = goToPage("tutorialLeft");
257+
258+ // Make sure we don't do anything if we don't pull the launcher
259+ // out much.
260+ var launcher = findChild(shell, "launcher");
261+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight);
262+ tryCompare(launcher, "state", ""); // should remain hidden
263+ tryCompare(left, "shown", true); // and we should still be on left
264+
265+ // Now drag out but not past launcher itself
266+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
267+
268+ waitForPage("tutorialLeftFinish");
269+ }
270+
271+ function test_launcherLongDrag() {
272+ // goToPage does a normal launcher pull. But here we want to test
273+ // a full pull across the page.
274+
275+ var left = goToPage("tutorialLeft");
276+
277+ var launcher = findChild(shell, "launcher");
278+ touchFlick(shell, 0, halfHeight, shell.width, halfHeight);
279+
280+ var errorTextLabel = findChild(left, "errorTextLabel");
281+ var errorTitleLabel = findChild(left, "errorTitleLabel");
282+ tryCompare(launcher, "state", ""); // launcher goes away
283+ tryCompare(left, "shown", true); // still on left page
284+ tryCompare(errorTextLabel, "opacity", 1); // show error
285+ tryCompare(errorTitleLabel, "opacity", 1); // show error
286+ }
287+
288+ function test_launcherDragBack() {
289+ // goToPage does a full launcher pull. But here we test pulling
290+ // all the way out, then dragging back into place.
291+
292+ var left = goToPage("tutorialLeft");
293+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight, true, false);
294+ touchFlick(shell, halfWidth, halfHeight, 0, halfHeight, false, true);
295+
296+ tryCompare(left, "shown", true); // and we should still be on left
297+ }
298+
299+ function test_spread() {
300+ // Unfortunately, most of what we want to test of the spread is
301+ // "did it render correctly?" but that's hard to test. So instead,
302+ // just poke and prod it a little bit to see if some of the values
303+ // we'd expect to be correct, are so.
304+
305+ var right = goToPage("tutorialRight");
306+ var stage = findChild(right, "stage");
307+ var delegate0 = findChild(right, "appDelegate0");
308+
309+ tryCompare(stage, "dragProgress", 0);
310+ touchFlick(shell, shell.width, halfHeight, shell.width * 0.8, halfHeight, true, false);
311+ verify(stage.dragProgress > 0);
312+ compare(stage.dragProgress, -delegate0.xTranslate);
313+ touchFlick(shell, shell.width * 0.8, halfHeight, shell.width, halfHeight, false, true);
314+ tryCompare(stage, "dragProgress", 0);
315+
316+ tryCompare(delegate0, "x", shell.width);
317+
318+ var screenshotImage = findChild(right, "screenshotImage");
319+ tryCompare(screenshotImage, "source", Qt.resolvedUrl("../../../qml/Tutorial/graphics/facebook.png"));
320+ tryCompare(screenshotImage, "visible", true);
321+ }
322+
323+ function test_bottomShortDrag() {
324+ var bottom = goToPage("tutorialBottom");
325+
326+ touchFlick(shell, halfWidth, shell.height, halfWidth, shell.height * 0.8);
327+
328+ var errorTextLabel = findChild(bottom, "errorTextLabel");
329+ var errorTitleLabel = findChild(bottom, "errorTitleLabel");
330+ tryCompare(bottom, "shown", true); // still on bottom page
331+ tryCompare(errorTextLabel, "opacity", 1); // show error
332+ tryCompare(errorTitleLabel, "opacity", 1); // show error
333+ }
334+
335+ function test_interrupted() {
336+ goToPage("tutorialLeft");
337+ ApplicationManager.startApplication("dialer-app");
338+ checkFinished();
339+ }
340+ }
341+}