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 tutorialLeft = findChild(shell, "tutorialLeft");
271 var tutorialLeftTimer = findInvisibleChild(tutorialLeft, "tutorialLeftTimer");
272 tutorialLeftTimer.interval = 0;
273 tryCompare(tutorialLeft, "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 //topLevelSurfaceList.inputMethodSurface.setState(Mir.MinimizedState);
312 callManager.foregroundCall = null;
313 AccountsService.demoEdges = false;
314 AccountsService.demoEdgesCompleted = [];
315 AccountsService.demoEdges = true;
317 LightDM.Greeter.hideGreeter();
319 stage = findChild(shell, "stage"); // from StageTestCase
321 startApplication("unity8-dash");
324 function loadShell(state) {
329 function ensureInputMethodSurface() {
330 var surfaceManager = findInvisibleChild(shell, "surfaceManager");
331 verify(surfaceManager);
332 surfaceManager.createInputMethodSurface();
334 tryCompareFunction(function() { return topLevelSurfaceList.inputMethodSurface !== null }, true);
337 function swipeAwayGreeter() {
338 var coverPage = findChild(shell, "coverPage");
339 tryCompare(coverPage, "showProgress", 1);
341 touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
343 // wait until the animation has finished
344 var greeter = findChild(shell, "greeter");
345 tryCompare(greeter, "required", false);
346 waitForRendering(greeter);
349 function openTutorialTop() {
350 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
351 var tutorialTop = findChild(shell, "tutorialTop");
352 var tutorialTopTimer = findInvisibleChild(tutorialTop, "tutorialTopTimer");
354 tutorialTopTimer.interval = 1;
355 AccountsService.demoEdgesCompleted = ["left"];
357 tryCompare(tutorialLeftLoader, "active", false);
358 tryCompare(tutorialTop, "shown", true);
359 tryCompare(tutorialTop, "opacity", 1);
362 function openTutorialLeftLong() {
363 var tutorialLeftLong = findChild(shell, "tutorialLeftLong");
364 var tutorialLeftLongTimer = findChild(tutorialLeftLong, "tutorialLeftLongTimer");
366 AccountsService.demoEdgesCompleted = ["left", "top"];
367 ApplicationManager.startApplication("gallery-app");
369 tutorialLeftLongTimer.interval = 1;
370 tryCompare(tutorialLeftLong, "shown", true);
371 tryCompare(tutorialLeftLong, "opacity", 1);
374 function openTutorialRight() {
375 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
376 var tutorialRight = findChild(shell, "tutorialRight");
377 var tutorialRightTimer = findChild(tutorialRight, "tutorialRightTimer");
379 AccountsService.demoEdgesCompleted = ["left", "top", "left-long"];
380 ApplicationManager.startApplication("gallery-app");
381 ApplicationManager.startApplication("facebook-webapp");
382 ApplicationManager.startApplication("camera-app");
384 tryCompare(tutorialLeftLoader, "active", false);
385 tutorialRightTimer.interval = 1;
386 tryCompare(tutorialRight, "shown", true);
387 tryCompare(tutorialRight, "opacity", 1);
390 function test_tutorialLeftEdges() {
391 var tutorial = findChild(shell, "tutorial");
392 var tutorialLeft = findChild(tutorial, "tutorialLeft");
393 var launcher = findChild(shell, "launcher");
394 var stage = findChild(shell, "stage");
395 var panel = findChild(shell, "panel");
397 verify(tutorial.running);
398 verify(tutorial.launcherEnabled);
399 verify(!tutorial.spreadEnabled);
400 verify(!tutorial.panelEnabled);
401 verify(tutorialLeft.shown);
402 verify(launcher.available);
403 verify(!stage.spreadEnabled);
404 verify(!panel.indicators.available);
407 function test_tutorialLeftFinish() {
408 var tutorial = findChild(shell, "tutorial");
409 var tutorialLeftLoader = findChild(tutorial, "tutorialLeftLoader");
410 var launcher = findChild(shell, "launcher");
412 touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
414 tryCompare(tutorialLeftLoader, "shown", false);
415 tryCompare(AccountsService, "demoEdgesCompleted", ["left"]);
416 tryCompare(launcher, "state", "visible");
419 function test_tutorialLeftShortDrag() {
420 // Here we want to test just barely pulling the launcher out and
421 // letting go (i.e. not triggering the "progress" property of
423 var tutorialLeft = findChild(shell, "tutorialLeft");
424 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
425 var launcher = findChild(shell, "launcher");
427 // Confirm fade during drag
428 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight, true, false);
429 // compare opacity with a bound rather than hard 0.6 because progress doesn't
430 // always match the drag perfectly (takes a moment for drag to kick in)
431 tryCompareFunction(function() {
432 return tutorialLeft.opacity >= 0.6 && tutorialLeft.opacity < 0.8;
434 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight, false, true);
437 // Make sure we don't do anything if we don't pull the launcher
439 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight);
440 tryCompare(launcher, "state", ""); // should remain hidden
441 tryCompare(tutorialLeftLoader, "shown", true); // and we should still be on left
444 // Now drag out but not past launcher itself
445 touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
447 tryCompare(tutorialLeftLoader, "shown", false);
448 tryCompare(AccountsService, "demoEdgesCompleted", ["left"]);
449 tryCompare(launcher, "state", "visible");
452 function test_tutorialLeftLongFinish() {
453 AccountsService.demoEdgesCompleted = ["left", "top"];
455 var tutorialLeftLongTimer = findInvisibleChild(shell, "tutorialLeftLongTimer");
456 tutorialLeftLongTimer.interval = 1;
458 ApplicationManager.startApplication("gallery-app");
460 var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
461 tryCompare(tutorialLeftLongLoader, "shown", true);
463 touchFlick(shell, 0, halfHeight, shell.width, halfHeight);
465 tryCompare(tutorialLeftLongLoader, "shown", false);
466 tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top", "left-long"]);
469 function test_tutorialLeftAutoSkipped() {
470 // Test that we skip the tutorial if user uses left edge themselves
472 var tutorialLeft = findChild(shell, "tutorialLeft");
473 LightDM.Greeter.showGreeter();
474 tryCompare(tutorialLeft, "visible", false);
475 compare(AccountsService.demoEdgesCompleted, []);
477 touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
478 tryCompare(AccountsService, "demoEdgesCompleted", ["left"]);
481 function test_tutorialLongLeftSwipeDisabled() {
482 // Test that a long left swipe is disabled until we get to or pass
483 // the long left tutorial.
484 var tutorial = findChild(shell, "tutorial");
486 verify(!tutorial.launcherLongSwipeEnabled);
488 openTutorialLeftLong();
489 verify(tutorial.launcherLongSwipeEnabled);
492 verify(tutorial.launcherLongSwipeEnabled);
494 AccountsService.demoEdges = false;
495 tryCompare(tutorial, "running", false);
496 verify(tutorial.launcherLongSwipeEnabled);
499 function test_tutorialTopEdges() {
500 var tutorial = findChild(shell, "tutorial");
501 var tutorialTop = findChild(tutorial, "tutorialTop");
502 var launcher = findChild(shell, "launcher");
503 var stage = findChild(shell, "stage");
504 var panel = findChild(shell, "panel");
508 tryCompare(tutorial, "running", true);
509 verify(!tutorial.launcherEnabled);
510 verify(!tutorial.spreadEnabled);
511 verify(tutorial.panelEnabled);
512 verify(tutorialTop.shown);
513 verify(!launcher.available);
514 verify(!stage.spreadEnabled);
515 verify(panel.indicators.available);
518 function test_tutorialTopFinish() {
519 var tutorial = findChild(shell, "tutorial");
520 var tutorialTopLoader = findChild(tutorial, "tutorialTopLoader");
521 var panel = findChild(shell, "panel");
524 touchFlick(shell, halfWidth, 0, halfWidth, shell.height);
526 tryCompare(tutorialTopLoader, "shown", false);
527 tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top"]);
528 tryCompare(panel.indicators, "fullyOpened", true);
531 function test_tutorialTopShortDrag() {
532 var tutorial = findChild(shell, "tutorial");
533 var tutorialTop = findChild(tutorial, "tutorialTop");
534 var panel = findChild(shell, "panel");
537 touchFlick(shell, halfWidth, 0, halfWidth, shell.height * 0.4, true, false);
538 // compare opacity with a bound rather than hard 0.6 because progress doesn't
539 // always match the drag perfectly (takes a moment for drag to kick in)
540 tryCompareFunction(function() {
541 return tutorialTop.opacity >= 0.5 && tutorialTop.opacity < 0.7;
543 touchFlick(shell, halfWidth, 0, halfWidth, shell.height * 0.4, false, true);
545 compare(tutorialTop.shown, true);
548 function test_tutorialTopAutoSkipped() {
549 // Test that we skip the tutorial if user uses top edge themselves
551 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
552 var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
553 AccountsService.demoEdgesCompleted = ["left"];
554 tryCompare(tutorialLeftLoader, "active", false);
555 verify(!tutorialTopLoader.shown);
557 touchFlick(shell, halfWidth, 0, halfWidth, shell.height);
558 tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top"]);
561 function test_tutorialRightEdges() {
562 var tutorial = findChild(shell, "tutorial");
563 var tutorialRight = findChild(tutorial, "tutorialRight");
564 var launcher = findChild(shell, "launcher");
565 var stage = findChild(shell, "stage");
566 var panel = findChild(shell, "panel");
570 tryCompare(tutorial, "running", true);
571 verify(!tutorial.launcherEnabled);
572 verify(tutorial.spreadEnabled);
573 verify(!tutorial.panelEnabled);
574 verify(tutorialRight.shown);
575 verify(!launcher.available);
576 verify(stage.spreadEnabled);
577 verify(!panel.indicators.available);
580 function test_tutorialRightFinish() {
582 touchFlick(shell, shell.width, halfHeight, 0, halfHeight);
584 tryCompare(AccountsService, "demoEdgesCompleted", ["left", "top", "left-long", "right"]);
585 tryCompare(AccountsService, "demoEdges", false);
588 function test_tutorialRightShortDrag() {
589 skip("This test interferes with test_tutorialRightAutoSkipped. If" +
590 " one runs before the other, the second one will fail. FIXME");
591 var tutorial = findChild(shell, "tutorial");
592 var tutorialRight = findChild(tutorial, "tutorialRight");
593 var stage = findChild(shell, "stage");
596 touchFlick(shell, shell.width, halfHeight, shell.width * 0.8, halfHeight, true, false);
597 // compare opacity with a bound rather than hard 0.6 because progress doesn't
598 // always match the drag perfectly (takes a moment for drag to kick in)
599 tryCompareFunction(function() {
600 return tutorialRight.opacity >= 0.6 && tutorialRight.opacity < 0.8;
602 touchFlick(shell, shell.width, halfHeight, shell.width * 0.8, halfHeight, false, true);
604 compare(tutorialRight.shown, true);
607 function test_tutorialRightDelay() {
608 // Test that if we exit the top tutorial, we don't immediately
609 // jump into right tutorial.
610 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
611 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
614 ApplicationManager.startApplication("gallery-app");
615 ApplicationManager.startApplication("facebook-webapp");
616 ApplicationManager.startApplication("camera-app");
617 tryCompare(ApplicationManager, "count", 4);
619 AccountsService.demoEdgesCompleted = ["left", "top", "left-long"];
620 verify(tutorialRightTimer.running);
621 verify(!tutorialRightLoader.shown);
622 tutorialRightTimer.interval = 1;
623 tryCompare(tutorialRightLoader, "shown", true);
626 function test_tutorialRightAutoSkipped() {
627 // Test that we skip the tutorial if user uses right edge themselves
629 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
630 AccountsService.demoEdgesCompleted = ["left"];
631 tryCompare(tutorialLeftLoader, "active", false);
633 touchFlick(shell, shell.width, halfHeight, 0, halfHeight);
634 tryCompare(AccountsService, "demoEdgesCompleted", ["left", "right"]);
637 function test_activeCallInterruptsTutorial() {
638 var tutorialLeft = findChild(shell, "tutorialLeft");
639 verify(tutorialLeft.shown);
640 verify(!tutorialLeft.paused);
642 callManager.foregroundCall = phoneCall;
643 verify(!tutorialLeft.shown);
644 verify(tutorialLeft.paused);
645 tryCompare(tutorialLeft, "visible", false);
647 callManager.foregroundCall = null;
648 tryCompare(tutorialLeft, "shown", true);
649 verify(!tutorialLeft.paused);
652 function test_greeterInterruptsTutorial() {
653 var tutorialLeft = findChild(shell, "tutorialLeft");
654 verify(tutorialLeft.shown);
655 verify(!tutorialLeft.paused);
657 LightDM.Greeter.showGreeter();
658 verify(!tutorialLeft.shown);
659 verify(tutorialLeft.paused);
660 tryCompare(tutorialLeft, "visible", false);
662 LightDM.Greeter.hideGreeter();
663 tryCompare(tutorialLeft, "shown", true);
664 verify(!tutorialLeft.paused);
667 function test_interruptionChecksReadyStateWhenDone() {
668 // If we're done with an interruption (like active call), make sure
669 // that we don't blindly resume the tutorial -- our trigger
670 // conditions still need to be met. For example, there need to be
671 // enough apps open for the right edge tutorial.
675 var tutorialRight = findChild(shell, "tutorialRight");
676 verify(tutorialRight.isReady);
677 verify(tutorialRight.shown);
678 verify(!tutorialRight.paused);
680 callManager.foregroundCall = phoneCall;
682 callManager.foregroundCall = null;
684 verify(!tutorialRight.isReady);
685 verify(!tutorialRight.shown);
686 verify(!tutorialRight.paused);
687 compare(AccountsService.demoEdgesCompleted, ["left", "top", "left-long"]);
690 function test_desktopOnlyShowsTutorialRight() {
691 loadShell("desktop");
693 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
694 var tutorialTopLoader = findChild(shell, "tutorialTopLoader");
695 var tutorialLeftLongLoader = findChild(shell, "tutorialLeftLongLoader");
696 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
697 verify(!tutorialLeftLoader.active);
698 verify(!tutorialTopLoader.active);
699 verify(!tutorialLeftLongLoader.active);
700 verify(tutorialRightLoader.active);
701 compare(AccountsService.demoEdgesCompleted, []);
703 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
704 tutorialRightTimer.interval = 1;
706 ApplicationManager.startApplication("dialer-app");
707 ApplicationManager.startApplication("camera-app");
708 ApplicationManager.startApplication("facebook-webapp");
709 tryCompare(tutorialRightLoader.item, "isReady", true);
711 tryCompare(tutorialRightLoader, "shown", true);
714 function test_desktopTutorialRightFinish() {
715 loadShell("desktop");
717 var tutorialRightLoader = findChild(shell, "tutorialRightLoader");
718 var tutorialRightTimer = findInvisibleChild(tutorialRightLoader, "tutorialRightTimer");
719 tutorialRightTimer.interval = 1;
720 ApplicationManager.startApplication("dialer-app");
721 ApplicationManager.startApplication("camera-app");
722 ApplicationManager.startApplication("facebook-webapp");
723 tryCompare(tutorialRightLoader, "shown", true);
725 var cursor = findChild(shell, "cursor");
726 mouseMove(shell, shell.width, shell.height / 2);
727 cursor.pushedRightBoundary(units.gu(8), 0);
728 tryCompare(tutorialRightLoader, "shown", false);
730 tryCompare(AccountsService, "demoEdges", false);
733 function test_oskDoesNotHideTutorial() {
734 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
735 verify(tutorialLeftLoader.shown);
737 ensureInputMethodSurface();
738 var surface = topLevelSurfaceList.inputMethodSurface;
739 surface.requestState(Mir.RestoredState);
741 var inputMethod = findInvisibleChild(shell, "inputMethod");
742 tryCompare(inputMethod, "visible", true);
744 verify(tutorialLeftLoader.shown);
747 function test_oskDelaysTutorial() {
748 var tutorial = findChild(shell, "tutorial");
749 verify(!tutorial.delayed);
751 ensureInputMethodSurface();
752 topLevelSurfaceList.inputMethodSurface.requestState(Mir.RestoredState);
754 tryCompare(tutorial, "delayed", true);
757 function test_notificationDelaysTutorial() {
758 var tutorial = findChild(shell, "tutorial");
759 verify(!tutorial.delayed);
761 var notifications = findChild(shell, "notificationList");
762 notifications.model = mockNotificationsModel;
763 addNotification(); // placeholder
766 tryCompare(tutorial, "delayed", true);
769 function test_dialogDelaysTutorial() {
770 var tutorial = findChild(shell, "tutorial");
771 verify(!tutorial.delayed);
773 var dialogs = findChild(shell, "dialogs");
774 dialogs.showPowerDialog();
776 verify(tutorial.delayed);
779 function test_delayedTutorial() {
780 var tutorial = findChild(shell, "tutorial");
781 var tutorialTop = findChild(tutorial, "tutorialTop");
782 var tutorialTopTimer = findChild(tutorialTop, "tutorialTopTimer");
784 // Get top tutorial ready
785 AccountsService.demoEdgesCompleted = ["left"];
786 verify(tutorialTop.isReady);
787 verify(tutorialTopTimer.running);
789 // Confirm that we become unready when delayed, but timer still goes
790 tutorial.delayed = true;
791 verify(!tutorialTop.isReady);
792 verify(tutorialTopTimer.running);
794 // Confirm that we don't open the tutorial when delayed
795 tutorialTopTimer.interval = 1;
797 verify(!tutorialTop.shown);
799 // Confirm we go back to normal when undelayed
800 tutorial.delayed = false;
801 tryCompare(tutorialTop, "shown", true);
804 function test_accountsServiceSettings() {
805 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
806 verify(tutorialLeftLoader.shown);
808 AccountsService.demoEdges = false;
809 verify(findChild(shell, "tutorialLeftLoader") == null);
811 AccountsService.demoEdges = true;
812 tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
813 var tutorialLeftTimer = findChild(tutorialLeftLoader, "tutorialLeftTimer");
814 verify(tutorialLeftTimer.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