2 * Copyright (C) 2013-2016 Canonical, Ltd.
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.
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.
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/>.
19import AccountsService 0.1
20import LightDMController 0.1
21import LightDM.FullLightDM 0.1 as LightDM
22import Ubuntu.Components 1.3
23import Ubuntu.Components.ListItems 1.3
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Application 0.1
26import Unity.Notifications 1.0
27import Unity.Test 0.1 as UT
31import "../../../qml/Components"
35 color: UbuntuColors.lightGrey
36 width: units.gu(100) + buttons.width
40 id: applicationArguments
42 function hasGeometry() {
57 phoneNumber: "+447812221111"
66 id: mockNotificationsModel
69 return mockNotification.createObject(mockNotificationsModel)
73 function addNotification() {
75 type: Notification.Confirmation,
84 mockNotificationsModel.append(n);
87 Component.onCompleted: {
88 // must set the mock mode before loading the Shell
89 LightDMController.userMode = "single-pin";
90 shellLoader.active = true;
95 anchors.left: root.left
96 anchors.right: buttons.left
98 anchors.bottom: root.bottom
104 anchors.horizontalCenter: parent.horizontalCenter
105 anchors.top: parent.top
106 anchors.bottom: parent.bottom
108 property int shellOrientation: Qt.PortraitOrientation
109 property int nativeOrientation: Qt.PortraitOrientation
110 property int primaryOrientation: Qt.PortraitOrientation
112 state: modeSelector.model[modeSelector.selectedIndex]
126 shellOrientation: Qt.LandscapeOrientation
127 nativeOrientation: Qt.LandscapeOrientation
128 primaryOrientation: Qt.LandscapeOrientation
140 property bool itemDestroyed: false
141 sourceComponent: Component {
143 usageScenario: shellLoader.state
146 property string indicatorProfile: "phone"
147 orientation: shellLoader.shellOrientation
148 orientations: Orientations {
149 native_: shellLoader.nativeOrientation
150 primary: shellLoader.primaryOrientation
154 Component.onDestruction: {
155 shellLoader.itemDestroyed = true;
164 color: UbuntuColors.darkGrey
166 anchors.top: root.top
167 anchors.bottom: root.bottom
168 anchors.right: root.right
171 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
174 anchors { left: parent.left; right: parent.right }
178 if (shellLoader.status !== Loader.Ready)
181 var greeter = testCase.findChild(shellLoader.item, "greeter");
190 anchors { left: parent.left; right: parent.right }
192 text: "Restart Tutorial"
194 if (shellLoader.status !== Loader.Ready)
197 AccountsService.demoEdges = false;
198 AccountsService.demoEdgesCompleted = [];
199 AccountsService.demoEdges = true;
205 anchors { left: parent.left; right: parent.right }
209 callManager.foregroundCall = phoneCall;
211 callManager.foregroundCall = null;
217 anchors.verticalCenter: parent.verticalCenter
222 anchors { left: parent.left; right: parent.right }
224 activeFocusOnPress: false
226 var surface = topLevelSurfaceList.inputMethodSurface;
228 surface.requestState(Mir.RestoredState);
230 surface.requestState(Mir.MinimizedState);
236 anchors.verticalCenter: parent.verticalCenter
242 anchors { left: parent.left; right: parent.right }
243 activeFocusOnPress: false
245 model: ["phone", "tablet", "desktop"]
255 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
256 property real halfWidth: shell ? shell.width / 2 : 0
257 property real halfHeight: shell ? shell.height / 2 : 0
261 topLevelSurfaceList = testCase.findInvisibleChild(shell, "topLevelSurfaceList");
263 topLevelSurfaceList = null;
270 var tutorialTop = findChild(shell, "tutorialTop");
271 var tutorialTopTimer = findInvisibleChild(tutorialTop, "tutorialTopTimer");
272 tutorialTopTimer.interval = 0;
273 tryCompare(tutorialTop, "opacity", 1);
277 resetLoader("phone");
280 function resetLoader(state) {
281 shellLoader.itemDestroyed = false;
283 shellLoader.active = false;
284 shellLoader.state = state;
286 tryCompare(shellLoader, "status", Loader.Null);
287 tryCompare(shellLoader, "item", null);
288 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
289 // item might still be alive. So if we set Loader.active back to true
290 // again right now we will get the very same Shell instance back. So no reload
291 // actually took place. Likely because Loader waits until the next event loop
292 // iteration to do its work. So to ensure the reload, we will wait until the
293 // Shell instance gets destroyed.
294 tryCompare(shellLoader, "itemDestroyed", true);
296 // kill all (fake) running apps
299 // reload our test subject to get it in a fresh state once again
300 shellLoader.active = true;
302 mockNotificationsModel.clear();
303 tryCompare(shellLoader, "status", Loader.Ready);
304 removeTimeConstraintsFromSwipeAreas(shellLoader.item);
307 function prepareShell() {
308 tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
310 WindowStateStorage.clear();
311 callManager.foregroundCall = null;
312 AccountsService.demoEdges = false;
313 AccountsService.demoEdgesCompleted = [];
314 AccountsService.demoEdges = true;
316 LightDM.Greeter.hideGreeter();
318 stage = findChild(shell, "stage"); // from StageTestCase
321 function loadShell(state) {
326 function ensureInputMethodSurface() {
327 var surfaceManager = findInvisibleChild(shell, "surfaceManager");
328 verify(surfaceManager);
329 surfaceManager.createInputMethodSurface();
331 tryCompareFunction(function() { return topLevelSurfaceList.inputMethodSurface !== null }, true);
334 function openTutorialLeft() {
335 var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
336 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
337 var tutorialLeft = findInvisibleChild(tutorialLeftLoader, "tutorialLeft");
338 var tutorialLeftTimer = findInvisibleChild(tutorialLeft, "tutorialLeftTimer");
340 tutorialLeftTimer.interval = 1;
341 AccountsService.demoEdgesCompleted = ["top"];
343 tryCompare(tutorialTopLoader, "active", false);
344 tryCompare(tutorialLeft, "shown", true);
345 tryCompare(tutorialLeft, "opacity", 1);
348 function openTutorialLeftLong() {
349 var tutorialLeftLong = findChild(shell, "tutorialLeftLong");
350 var tutorialLeftLongTimer = findChild(tutorialLeftLong, "tutorialLeftLongTimer");
352 AccountsService.demoEdgesCompleted = ["top", "left"];
354 tutorialLeftLongTimer.interval = 1;
355 tryCompare(tutorialLeftLong, "shown", true);
356 tryCompare(tutorialLeftLong, "opacity", 1);
359 function openTutorialRight() {
360 var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
361 var tutorialRight = findChild(shell, "tutorialRight");
362 var tutorialRightTimer = findChild(tutorialRight, "tutorialRightTimer");
364 AccountsService.demoEdgesCompleted = ["top", "left", "left-drawer"];
365 ApplicationManager.startApplication("facebook-webapp");
367 tryCompare(tutorialLeftLongLoader, "active", false);
368 tutorialRightTimer.interval = 1;
369 tryCompare(tutorialRight, "shown", true);
370 tryCompare(tutorialRight, "opacity", 1);
373 function longLeftSwipe() {
374 // Perform a long enough swipe to cause the Drawer to appear
375 var launcher = findChild(shell, "launcher");
376 var startX = launcher.dragAreaWidth/2;
377 var startY = launcher.height/2;
380 startX+units.gu(35), startY);
383 function test_tutorialLeftEdges() {
384 var tutorial = findChild(shell, "tutorial");
385 var tutorialLeft = findChild(tutorial, "tutorialLeft");
386 var launcher = findChild(shell, "launcher");
387 var stage = findChild(shell, "stage");
388 var panel = findChild(shell, "panel");
392 verify(tutorial.running);
393 verify(tutorial.launcherEnabled);
394 verify(!tutorial.spreadEnabled);
395 verify(!tutorial.panelEnabled);
396 verify(tutorialLeft.shown);
397 verify(launcher.available);
398 verify(!stage.spreadEnabled);
399 verify(!panel.indicators.available);
402 function test_tutorialLeftFinish() {
403 var tutorial = findChild(shell, "tutorial");
404 var tutorialLeftLoader = findChild(tutorial, "tutorialLeftLoader");
405 var launcher = findChild(shell, "launcher");
409 touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
411 tryCompare(tutorialLeftLoader, "shown", false);
412 tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left"]);
413 tryCompare(launcher, "state", "visible");
416 function test_tutorialLeftShortDrag() {
417 // Here we want to test just barely pulling the launcher out and
418 // letting go (i.e. not triggering the "progress" property of
420 var tutorialLeft = findChild(shell, "tutorialLeft");
421 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
422 var launcher = findChild(shell, "launcher");
426 // Confirm fade during drag
427 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight, true, false);
428 // compare opacity with a bound rather than hard 0.6 because progress doesn't
429 // always match the drag perfectly (takes a moment for drag to kick in)
430 tryCompareFunction(function() {
431 return tutorialLeft.opacity >= 0.6 && tutorialLeft.opacity < 0.8;
433 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight, false, true);
436 // Make sure we don't do anything if we don't pull the launcher
438 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight);
439 tryCompare(launcher, "state", ""); // should remain hidden
440 tryCompare(tutorialLeftLoader, "shown", true); // and we should still be on left
443 // Now drag out but not past launcher itself
444 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
446 tryCompare(tutorialLeftLoader, "shown", false);
447 tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left"]);
448 tryCompare(launcher, "state", "visible");
451 function test_tutorialLeftLongFinish() {
452 var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
453 openTutorialLeftLong();
455 tryCompare(tutorialLeftLongLoader, "shown", true);
457 touchFlick(shell, 0, halfHeight, shell.width, halfHeight);
459 tryCompare(tutorialLeftLongLoader, "shown", false);
460 tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left", "left-drawer"]);
463 function test_tutorialLeftAutoSkipped() {
464 // Test that we skip the tutorial if user uses left edge themselves
465 var launcher = findChild(shell, "launcher");
467 AccountsService.demoEdgesCompleted = ["top"]
468 var tutorialLeft = findChild(shell, "tutorialLeft");
470 tryCompare(tutorialLeft, "visible", false);
471 compare(AccountsService.demoEdgesCompleted, ["top"]);
473 touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
474 tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left"]);
477 function test_tutorialLongLeftSwipeDisabled() {
478 // Test that a long left swipe is disabled until we get to or pass
479 // the long left tutorial.
480 var tutorial = findChild(shell, "tutorial");
481 var launcher = findChild(shell, "launcher");
482 var drawer = findChild(launcher, "drawer");
484 // Waiting for top tutorial, the Launcher should be unavailable
486 tryCompare(launcher, "state", "");
488 // Swipe the left edge far enough to open the Drawer. Only the
489 // Launcher should appear.
492 tryCompare(launcher, "state", "visible");
494 // Once the Drawer tutorial is open, the Drawer should appear on
496 openTutorialLeftLong();
498 tryCompare(launcher, "state", "drawer");
501 // That should continue after the Tutorial is no more
502 AccountsService.demoEdges = false;
503 tryCompare(tutorial, "running", false);
505 tryCompare(launcher, "state", "drawer");
508 function test_tutorialTopEdges() {
509 var tutorial = findChild(shell, "tutorial");
510 var tutorialTop = findChild(tutorial, "tutorialTop");
511 var launcher = findChild(shell, "launcher");
512 var stage = findChild(shell, "stage");
513 var panel = findChild(shell, "panel");
515 tryCompare(tutorial, "running", true);
516 verify(!tutorial.launcherEnabled);
517 verify(!tutorial.spreadEnabled);
518 verify(tutorial.panelEnabled);
519 verify(tutorialTop.shown);
520 verify(!launcher.available);
521 verify(!stage.spreadEnabled);
522 verify(panel.indicators.available);
525 function test_tutorialTopFinish() {
526 var tutorial = findChild(shell, "tutorial");
527 var tutorialTopLoader = findChild(tutorial, "tutorialTopLoader");
528 var panel = findChild(shell, "panel");
530 touchFlick(shell, halfWidth, 0, halfWidth, shell.height);
532 tryCompare(tutorialTopLoader, "shown", false);
533 tryCompare(AccountsService, "demoEdgesCompleted", ["top"]);
534 tryCompare(panel.indicators, "fullyOpened", true);
537 function test_tutorialTopShortDrag() {
538 var tutorial = findChild(shell, "tutorial");
539 var tutorialTop = findChild(tutorial, "tutorialTop");
540 var panel = findChild(shell, "panel");
542 touchFlick(shell, halfWidth, 0, halfWidth, shell.height * 0.4, true, false);
543 // compare opacity with a bound rather than hard 0.6 because progress doesn't
544 // always match the drag perfectly (takes a moment for drag to kick in)
545 tryCompareFunction(function() {
546 return tutorialTop.opacity >= 0.5 && tutorialTop.opacity < 0.7;
548 touchFlick(shell, halfWidth, 0, halfWidth, shell.height * 0.4, false, true);
550 compare(tutorialTop.shown, true);
553 function test_tutorialRightEdges() {
554 var tutorial = findChild(shell, "tutorial");
555 var tutorialRight = findChild(tutorial, "tutorialRight");
556 var launcher = findChild(shell, "launcher");
557 var stage = findChild(shell, "stage");
558 var panel = findChild(shell, "panel");
562 tryCompare(tutorial, "running", true);
563 verify(!tutorial.launcherEnabled);
564 verify(tutorial.spreadEnabled);
565 verify(!tutorial.panelEnabled);
566 verify(tutorialRight.shown);
567 verify(!launcher.available);
568 verify(stage.spreadEnabled);
569 verify(!panel.indicators.available);
572 function test_tutorialRightFinish() {
574 touchFlick(shell, shell.width, halfHeight, 0, halfHeight);
576 tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left", "left-drawer", "right"]);
577 tryCompare(AccountsService, "demoEdges", false);
580 function test_tutorialRightShortDrag() {
581 var tutorial = findChild(shell, "tutorial");
582 var tutorialRight = findChild(tutorial, "tutorialRight");
583 var stage = findChild(shell, "stage");
586 touchFlick(shell, shell.width, halfHeight, shell.width * 0.8, halfHeight, true, false);
587 // compare opacity with a bound rather than hard 0.6 because progress doesn't
588 // always match the drag perfectly (takes a moment for drag to kick in)
590 return tutorialRight.opacity >= 0.6 && tutorialRight.opacity < 0.8;
592 touchFlick(shell, shell.width, halfHeight, shell.width * 0.8, halfHeight, false, true);
594 compare(tutorialRight.shown, true);
597 function test_tutorialRightDelay() {
598 // Test that if we exit the top tutorial, we don't immediately
599 // jump into right tutorial.
600 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
601 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
603 ApplicationManager.startApplication("gallery-app");
604 tryCompare(ApplicationManager, "count", 1);
606 AccountsService.demoEdgesCompleted = ["top", "left", "left-drawer"];
607 verify(tutorialRightTimer.running);
608 verify(!tutorialRightLoader.shown);
609 tutorialRightTimer.interval = 1;
610 tryCompare(tutorialRightLoader, "shown", true);
613 function test_tutorialRightAutoSkipped() {
614 // Test that we skip the tutorial if user uses right edge themselves
616 var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
617 var tutorialRight = findChild(shell, "tutorialRight");
618 var tutorialRightTimer = findChild(tutorialRight, "tutorialRightTimer");
620 tutorialRightTimer.interval = 9999999;
622 ApplicationManager.startApplication("facebook-webapp");
623 tryCompare(ApplicationManager, "count", 1);
625 AccountsService.demoEdgesCompleted = ["top", "left", "left-drawer"];
626 tryCompare(tutorialLeftLongLoader, "skipped", true);
627 verify(tutorialRightTimer.running);
628 tryCompare(tutorialRight, "isReady", true);
630 // If the app isn't fully started, it may interrupt our attempt to swipe
633 touchFlick(shell, shell.width, halfHeight, 0, halfHeight);
634 tryCompare(AccountsService, "demoEdgesCompleted", ["top", "left", "left-drawer", "right"]);
637 function test_activeCallInterruptsTutorial() {
638 var tutorialLeft = findChild(shell, "tutorialLeft");
641 callManager.foregroundCall = phoneCall;
642 verify(!tutorialLeft.shown);
643 verify(tutorialLeft.paused);
644 tryCompare(tutorialLeft, "visible", false);
646 callManager.foregroundCall = null;
647 tryCompare(tutorialLeft, "shown", true);
648 verify(!tutorialLeft.paused);
651 function test_greeterInterruptsTutorial() {
652 var tutorialTop = findChild(shell, "tutorialTop");
653 verify(tutorialTop.shown);
654 verify(!tutorialTop.paused);
656 LightDM.Greeter.showGreeter();
657 verify(!tutorialTop.shown);
658 verify(tutorialTop.paused);
659 tryCompare(tutorialTop, "visible", false);
661 LightDM.Greeter.hideGreeter();
662 tryCompare(tutorialTop, "shown", true);
663 verify(!tutorialTop.paused);
666 function test_interruptionChecksReadyStateWhenDone() {
667 // If we're done with an interruption (like active call), make sure
668 // that we don't blindly resume the tutorial -- our trigger
669 // conditions still need to be met. For example, there need to be
670 // enough apps open for the right edge tutorial.
674 var tutorialRight = findChild(shell, "tutorialRight");
675 verify(tutorialRight.isReady);
676 verify(tutorialRight.shown);
677 verify(!tutorialRight.paused);
679 callManager.foregroundCall = phoneCall;
681 callManager.foregroundCall = null;
683 verify(!tutorialRight.isReady);
684 verify(!tutorialRight.shown);
685 verify(!tutorialRight.paused);
686 compare(AccountsService.demoEdgesCompleted, ["top", "left", "left-drawer"]);
689 function test_desktopOnlyShowsTutorialRight() {
690 loadShell("desktop");
692 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
693 var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
694 var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
695 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
696 verify(!tutorialLeftLoader.active);
697 verify(!tutorialTopLoader.active);
698 verify(!tutorialLeftLongLoader.active);
699 verify(tutorialRightLoader.active);
700 compare(AccountsService.demoEdgesCompleted, []);
702 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
703 tutorialRightTimer.interval = 1;
705 ApplicationManager.startApplication("facebook-webapp");
706 tryCompare(tutorialRightLoader.item, "isReady", true);
708 tryCompare(tutorialRightLoader, "shown", true);
711 function test_desktopTutorialRightFinish() {
712 loadShell("desktop");
714 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
715 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
716 tutorialRightTimer.interval = 1;
717 ApplicationManager.startApplication("facebook-webapp");
718 tryCompare(tutorialRightLoader, "shown", true);
720 var cursor = findChild(shell, "cursor");
721 mouseMove(shell, shell.width, shell.height / 2);
722 cursor.pushedRightBoundary(units.gu(8), 0);
723 tryCompare(tutorialRightLoader, "shown", false);
725 tryCompare(AccountsService, "demoEdges", false);
728 function test_oskDoesNotHideTutorial() {
729 var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
730 verify(tutorialTopLoader.shown);
732 ensureInputMethodSurface();
733 var surface = topLevelSurfaceList.inputMethodSurface;
734 surface.requestState(Mir.RestoredState);
736 var inputMethod = findInvisibleChild(shell, "inputMethod");
737 tryCompare(inputMethod, "visible", true);
739 verify(tutorialTopLoader.shown);
742 function test_oskDelaysTutorial() {
743 var tutorial = findChild(shell, "tutorial");
744 verify(!tutorial.delayed);
746 ensureInputMethodSurface();
747 topLevelSurfaceList.inputMethodSurface.requestState(Mir.RestoredState);
749 tryCompare(tutorial, "delayed", true);
752 function test_notificationDelaysTutorial() {
753 var tutorial = findChild(shell, "tutorial");
754 verify(!tutorial.delayed);
756 var notifications = findChild(shell, "notificationList");
757 notifications.model = mockNotificationsModel;
758 addNotification(); // placeholder
761 tryCompare(tutorial, "delayed", true);
764 function test_dialogDelaysTutorial() {
765 var tutorial = findChild(shell, "tutorial");
766 verify(!tutorial.delayed);
768 var dialogs = findChild(shell, "dialogs");
769 dialogs.showPowerDialog();
771 verify(tutorial.delayed);
774 function test_delayedTutorial() {
775 var launcher = findChild(shell, "launcher");
776 var tutorial = findChild(shell, "tutorial");
777 var tutorialLeft = findChild(tutorial, "tutorialLeft");
778 var tutorialLeftTimer = findChild(tutorial, "tutorialLeftTimer");
779 // Since we'll be using the Launcher and Drawer, the left tutorial
780 // will be skipped entirely and we'll show the left long one.
781 var tutorialLeftLong = findChild(tutorial, "tutorialLeftLong");
782 var tutorialLeftLongTimer = findChild(tutorialLeftLong, "tutorialLeftLongTimer");
784 // Don't need these to start before we're ready
785 tutorialLeftTimer.interval = 999999;
786 tutorialLeftLongTimer.interval = 1;
788 // Get left tutorial ready to be skipped
789 AccountsService.demoEdgesCompleted = ["top"];
790 tryCompare(tutorialLeft, "isReady", true);
791 tryCompare(tutorialLeftTimer, "running", true);
793 // Confirm that we become unready when delayed, but timer still goes
794 tutorial.delayed = true;
795 tryCompare(tutorialLeft, "isReady", false);
796 tryCompare(tutorialLeftTimer, "running", true);
798 // Confirm that we don't open the tutorial when delayed
799 tutorialLeftTimer.interval = 1;
801 verify(!tutorialLeft.shown);
803 // Confirm the Launcher and Drawer can be opened when delayed
804 tryCompare(tutorial, "launcherEnabled", true);
805 tryCompare(tutorial, "launcherLongSwipeEnabled", true);
807 tryCompare(launcher, "state", "drawer");
810 // Confirm we go back to normal when undelayed
811 tutorial.delayed = false;
812 tryCompare(tutorialLeftLong, "shown", true);
815 function test_accountsServiceSettings() {
816 var tutorial = findChild(shell, "tutorial");
817 var tutorialTopLoader = findChild(tutorial, "tutorialTopLoader");
818 verify(tutorialTopLoader.shown);
820 AccountsService.demoEdges = false;
821 verify(findChild(tutorial, "tutorialTopLoader", 1) == null);
823 AccountsService.demoEdges = true;
824 tutorialTopLoader = findChild(shell, "tutorialTopLoader");
825 var tutorialTopTimer = findChild(tutorialTopLoader, "tutorialTopTimer");
826 verify(tutorialTopTimer.running);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list