on xenial help sign in
Add support for LightDM hints for manual logins and hiding normal users.

Approved by: Albert Astals Cid, Unity8 CI Bot

commitf94aa28a23f5fb700bfadcb31ab35cf10bd6f399 authorMichael Terry <michael.terry@canonical.com> dateTue 24 Jan 2017, 07:40 (10 years ago) parentf86906827fa2c9
13 files changed+323−37
plugins/LightDM/Greeter.cpp+34−2 plugins/LightDM/Greeter.h+3 plugins/LightDM/GreeterPrivate.h+8−6 plugins/LightDM/UsersModel.cpp+123−21 qml/Greeter/GreeterPrompt.qml+1 qml/Greeter/LoginList.qml+4−1 tests/mocks/liblightdm/MockController.cpp+28 tests/mocks/liblightdm/MockController.h+12 tests/mocks/liblightdm/MockGreeter.cpp+15−3 tests/mocks/liblightdm/MockUsersModel.cpp+4 tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+53−2 tests/qmltests/Greeter/tst_Greeter.qml+9−1 tests/qmltests/tst_Shell.qml+29−1
plugins/LightDM/Greeter.cpp+34−2
@@ -88,7 +88,16 @@ bool Greeter::isAuthenticated() const
8888QString Greeter::authenticationUser() const
8989{
9090 Q_D(const Greeter);
91− return d->m_greeter->authenticationUser();
91+ return d->cachedAuthUser;
92+}
93+
94+void Greeter::checkAuthenticationUser()
95+{
96+ Q_D(Greeter);
97+ if (d->cachedAuthUser != d->m_greeter->authenticationUser()) {
98+ d->cachedAuthUser = d->m_greeter->authenticationUser();
99+ Q_EMIT authenticationUserChanged();
100+ }
92101}
93102
94103QString Greeter::defaultSessionHint() const
@@ -119,6 +128,18 @@ bool Greeter::hasGuestAccount() const
119128 return d->m_greeter->hasGuestAccountHint();
120129}
121130
131+bool Greeter::showManualLoginHint() const
132+{
133+ Q_D(const Greeter);
134+ return d->m_greeter->showManualLoginHint();
135+}
136+
137+bool Greeter::hideUsersHint() const
138+{
139+ Q_D(const Greeter);
140+ return d->m_greeter->hideUsersHint();
141+}
142+
122143void Greeter::authenticate(const QString &username)
123144{
124145 Q_D(Greeter);
@@ -130,12 +151,14 @@ void Greeter::authenticate(const QString &username)
130151
131152 if (username == QStringLiteral("*guest")) {
132153 d->m_greeter->authenticateAsGuest();
154+ } else if (username == QStringLiteral("*other")) {
155+ d->m_greeter->authenticate(nullptr);
133156 } else {
134157 d->m_greeter->authenticate(username);
135158 }
136159
137160 Q_EMIT isAuthenticatedChanged();
138− Q_EMIT authenticationUserChanged();
161+ checkAuthenticationUser();
139162}
140163
141164void Greeter::respond(const QString &response)
@@ -155,6 +178,8 @@ void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptTyp
155178 Q_D(Greeter);
156179 d->wasPrompted = true;
157180
181+ checkAuthenticationUser(); // may have changed in liblightdm
182+
158183 bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));
159184
160185 // Strip prompt of any colons at the end
@@ -163,11 +188,18 @@ void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptTyp
163188 trimmedText.chop(1);
164189 }
165190
191+ if (trimmedText == "login") {
192+ // 'login' is provided untranslated by LightDM when asking for a manual
193+ // login username.
194+ trimmedText = gettext("Username");
195+ }
196+
166197 Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret, isDefaultPrompt);
167198}
168199
169200void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
170201{
202+ checkAuthenticationUser(); // may have changed in liblightdm
171203 Q_EMIT showMessage(text, type == QLightDM::Greeter::MessageTypeError);
172204}
173205
plugins/LightDM/Greeter.h+3
@@ -50,6 +50,8 @@ public:
5050 bool promptless() const;
5151 QString selectUser() const;
5252 bool hasGuestAccount() const;
53+ bool showManualLoginHint() const;
54+ bool hideUsersHint() const;
5355
5456public Q_SLOTS:
5557 void authenticate(const QString &username=QString());
@@ -83,4 +85,5 @@ private Q_SLOTS:
8385 void showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type);
8486 void showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type);
8587 void authenticationCompleteFilter();
88+ void checkAuthenticationUser();
8689};
plugins/LightDM/GreeterPrivate.h+8−6
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2015-2017 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,10 +14,13 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−#ifndef UNITY_GREETER_PRIVATE_H
18−#define UNITY_GREETER_PRIVATE_H
17+#pragma once
1918
20−#include <QLightDM/Greeter>
19+#include <QObject>
20+
21+namespace QLightDM {
22+ class Greeter;
23+}
2124
2225class GreeterPrivate
2326{
@@ -28,6 +31,7 @@ public:
2831 bool m_active;
2932 bool wasPrompted;
3033 bool promptless;
34+ QString cachedAuthUser;
3135
3236protected:
3337 Greeter * const q_ptr;
@@ -35,5 +39,3 @@ protected:
3539private:
3640 Q_DECLARE_PUBLIC(Greeter)
3741};
38−
39−#endif // UNITY_GREETER_PRIVATE_H
plugins/LightDM/UsersModel.cpp+123−21
@@ -39,29 +39,63 @@ public:
3939 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
4040
4141private:
42− int guestRow() const;
42+ struct CustomRow {
43+ QString name;
44+ QString realName;
45+ };
46+
47+ int sourceRowCount() const;
48+
49+ void updateGuestRow();
50+ void updateManualRow();
51+ void updateCustomRows();
52+
53+ void addCustomRow(const CustomRow &newRow);
54+ void removeCustomRow(const QString &rowName);
55+
56+ QList<CustomRow> m_customRows;
57+ bool m_updatingCustomRows;
4358};
4459
4560MangleModel::MangleModel(QObject* parent)
4661 : QIdentityProxyModel(parent)
62+ , m_updatingCustomRows(false)
4763{
4864 setSourceModel(new QLightDM::UsersModel(this));
65+
66+ updateCustomRows();
67+
68+ // Would be nice if there were a rowCountChanged signal in the base class.
69+ // We redo all custom rows on any row count change, because (A) some of
70+ // custom rows (manual login) use row count information and (B) when
71+ // testing, we use a modelReset signal as a way to indicate that a custom
72+ // row has been toggled off or on.
73+ connect(this, &QIdentityProxyModel::modelReset,
74+ this, &MangleModel::updateCustomRows);
75+ connect(this, &QIdentityProxyModel::rowsInserted,
76+ this, &MangleModel::updateCustomRows);
77+ connect(this, &QIdentityProxyModel::rowsRemoved,
78+ this, &MangleModel::updateCustomRows);
4979}
5080
5181QVariant MangleModel::data(const QModelIndex &index, int role) const
5282{
5383 QVariant variantData;
5484
55− if (index.row() == guestRow() && index.column() == 0) {
56− switch (role) {
57− case QLightDM::UsersModel::NameRole:
58− variantData = QStringLiteral("*guest"); break;
59− case QLightDM::UsersModel::RealNameRole:
60− variantData = gettext("Guest Session"); break;
61− case QLightDM::UsersModel::LoggedInRole:
62− variantData = false; break;
63− case QLightDM::UsersModel::SessionRole:
64− variantData = Greeter::instance()->defaultSessionHint(); break;
85+ if (index.row() >= rowCount())
86+ return QVariant();
87+
88+ bool isCustomRow = index.row() >= sourceRowCount();
89+ if (isCustomRow && index.column() == 0) {
90+ int customIndex = index.row() - sourceRowCount();
91+ if (role == QLightDM::UsersModel::NameRole) {
92+ variantData = m_customRows[customIndex].name;
93+ } else if (role == QLightDM::UsersModel::RealNameRole) {
94+ variantData = m_customRows[customIndex].realName;
95+ } else if (role == QLightDM::UsersModel::LoggedInRole) {
96+ variantData = false;
97+ } else if (role == QLightDM::UsersModel::SessionRole) {
98+ variantData = Greeter::instance()->defaultSessionHint();
6599 }
66100 } else {
67101 variantData = QIdentityProxyModel::data(index, role);
@@ -69,7 +103,7 @@ QVariant MangleModel::data(const QModelIndex &index, int role) const
69103
70104 // If user's real name is empty, switch to unix name
71105 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
72− variantData = QIdentityProxyModel::data(index, QLightDM::UsersModel::NameRole);
106+ variantData = data(index, QLightDM::UsersModel::NameRole);
73107 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
74108 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
75109 variantData = stringData;
@@ -78,25 +112,89 @@ QVariant MangleModel::data(const QModelIndex &index, int role) const
78112 return variantData;
79113}
80114
81−int MangleModel::guestRow() const
115+void MangleModel::addCustomRow(const CustomRow &newRow)
82116{
83− return Greeter::instance()->hasGuestAccount() ?
84− QIdentityProxyModel::rowCount() : -1;
117+ for (int i = 0; i < m_customRows.size(); i++) {
118+ if (m_customRows[i].name == newRow.name) {
119+ return; // we don't have custom rows that change content yet
120+ }
121+ }
122+
123+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
124+ m_customRows << newRow;
125+ endInsertRows();
85126}
86127
87−int MangleModel::rowCount(const QModelIndex &parent) const
128+void MangleModel::removeCustomRow(const QString &rowName)
88129{
89− auto count = QIdentityProxyModel::rowCount(parent);
130+ for (int i = 0; i < m_customRows.size(); i++) {
131+ if (m_customRows[i].name == rowName) {
132+ int rowNum = sourceRowCount() + i;
133+ beginRemoveRows(QModelIndex(), rowNum, rowNum);
134+ m_customRows.removeAt(i);
135+ endRemoveRows();
136+ break;
137+ }
138+ }
139+}
90140
91− if (Greeter::instance()->hasGuestAccount() && !parent.isValid())
92− count++;
141+void MangleModel::updateManualRow()
142+{
143+ bool hasAnotherEntry = sourceRowCount() > 0;
144+ for (int i = 0; !hasAnotherEntry && i < m_customRows.size(); i++) {
145+ if (m_customRows[i].name != QStringLiteral("*other")) {
146+ hasAnotherEntry = true;
147+ }
148+ }
93149
94− return count;
150+ // Show manual login if we are asked to OR if no other entry exists
151+ if (Greeter::instance()->showManualLoginHint() || !hasAnotherEntry)
152+ addCustomRow({QStringLiteral("*other"), gettext("Login")});
153+ else
154+ removeCustomRow(QStringLiteral("*other"));
155+}
156+
157+void MangleModel::updateGuestRow()
158+{
159+ if (Greeter::instance()->hasGuestAccount())
160+ addCustomRow({QStringLiteral("*guest"), gettext("Guest Session")});
161+ else
162+ removeCustomRow(QStringLiteral("*guest"));
163+}
164+
165+void MangleModel::updateCustomRows()
166+{
167+ // We update when rowCount changes, but we also insert/remove rows here.
168+ // So guard this function to avoid recursion.
169+ if (m_updatingCustomRows)
170+ return;
171+
172+ m_updatingCustomRows = true;
173+ updateGuestRow();
174+ updateManualRow();
175+ m_updatingCustomRows = false;
176+}
177+
178+int MangleModel::rowCount(const QModelIndex &parent) const
179+{
180+ if (parent.isValid())
181+ return 0;
182+ else
183+ return sourceRowCount() + m_customRows.size();
184+}
185+
186+int MangleModel::sourceRowCount() const
187+{
188+ return Greeter::instance()->hideUsersHint() ? 0 : sourceModel()->rowCount();
95189}
96190
97191QModelIndex MangleModel::index(int row, int column, const QModelIndex &parent) const
98192{
99− if (row == guestRow() && !parent.isValid()) {
193+ if (row >= rowCount())
194+ return QModelIndex();
195+
196+ bool isCustomRow = row >= sourceRowCount();
197+ if (isCustomRow && !parent.isValid()) {
100198 return createIndex(row, column);
101199 } else {
102200 return QIdentityProxyModel::index(row, column, parent);
@@ -124,6 +222,10 @@ bool UsersModel::lessThan(const QModelIndex &source_left, const QModelIndex &sou
124222 return false;
125223 if (rightName == QStringLiteral("*guest"))
126224 return true;
225+ if (leftName == QStringLiteral("*other"))
226+ return false;
227+ if (rightName == QStringLiteral("*other"))
228+ return true;
127229
128230 return UnitySortFilterProxyModelQML::lessThan(source_left, source_right);
129231}
qml/Greeter/GreeterPrompt.qml+1
@@ -194,6 +194,7 @@ FocusScope {
194194 // palette color, whereas we want raisedSecondaryText.
195195 Label {
196196 id: hint
197+ objectName: "promptHint"
197198 anchors {
198199 left: parent.left
199200 right: parent.right
qml/Greeter/LoginList.qml+4−1
@@ -201,7 +201,10 @@ StyledItem {
201201 // Add an offset to bottomMargin for any items below the highlight
202202 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
203203 }
204− text: realName
204+ text: userList.currentIndex === index
205+ && name === "*other"
206+ && LightDMService.greeter.authenticationUser !== ""
207+ ? LightDMService.greeter.authenticationUser : realName
205208 color: userList.currentIndex !== index ? theme.palette.normal.raised
206209 : theme.palette.normal.raisedText
207210
tests/mocks/liblightdm/MockController.cpp+28
@@ -70,6 +70,8 @@ void MockController::reset()
7070 setUserMode(userMode);
7171 setSessionMode("full");
7272 setNumSessions(numFullSessions());
73+ setShowManualLoginHint(false);
74+ setHideUsersHint(false);
7375}
7476
7577QString MockController::selectUserHint() const
@@ -111,6 +113,32 @@ void MockController::setHasGuestAccountHint(bool hasGuestAccountHint)
111113 }
112114}
113115
116+bool MockController::showManualLoginHint() const
117+{
118+ return m_showManualLoginHint;
119+}
120+
121+void MockController::setShowManualLoginHint(bool showManualLoginHint)
122+{
123+ if (m_showManualLoginHint != showManualLoginHint) {
124+ m_showManualLoginHint = showManualLoginHint;
125+ Q_EMIT showManualLoginHintChanged();
126+ }
127+}
128+
129+bool MockController::hideUsersHint() const
130+{
131+ return m_hideUsersHint;
132+}
133+
134+void MockController::setHideUsersHint(bool hideUsersHint)
135+{
136+ if (m_hideUsersHint != hideUsersHint) {
137+ m_hideUsersHint = hideUsersHint;
138+ Q_EMIT hideUsersHintChanged();
139+ }
140+}
141+
114142QString MockController::userMode() const
115143{
116144 return m_userMode;
tests/mocks/liblightdm/MockController.h+12
@@ -29,6 +29,8 @@ class Q_DECL_EXPORT MockController : public QObject
2929 Q_PROPERTY(QString selectUserHint READ selectUserHint WRITE setSelectUserHint NOTIFY selectUserHintChanged)
3030 Q_PROPERTY(bool selectGuestHint READ selectGuestHint WRITE setSelectGuestHint NOTIFY selectGuestHintChanged)
3131 Q_PROPERTY(bool hasGuestAccountHint READ hasGuestAccountHint WRITE setHasGuestAccountHint NOTIFY hasGuestAccountHintChanged)
32+ Q_PROPERTY(bool showManualLoginHint READ showManualLoginHint WRITE setShowManualLoginHint NOTIFY showManualLoginHintChanged)
33+ Q_PROPERTY(bool hideUsersHint READ hideUsersHint WRITE setHideUsersHint NOTIFY hideUsersHintChanged)
3234
3335 // single, single-pin, single-passphrase, full
3436 Q_PROPERTY(QString userMode READ userMode WRITE setUserMode NOTIFY userModeChanged)
@@ -59,6 +61,12 @@ public:
5961 bool hasGuestAccountHint() const;
6062 void setHasGuestAccountHint(bool hasGuestAccountHint);
6163
64+ bool showManualLoginHint() const;
65+ void setShowManualLoginHint(bool showManualLoginHint);
66+
67+ bool hideUsersHint() const;
68+ void setHideUsersHint(bool hideUsersHint);
69+
6270 QString userMode() const;
6371 void setUserMode(const QString &userMode);
6472
@@ -88,6 +96,8 @@ Q_SIGNALS:
8896 void selectUserHintChanged();
8997 void selectGuestHintChanged();
9098 void hasGuestAccountHintChanged();
99+ void showManualLoginHintChanged();
100+ void hideUsersHintChanged();
91101 void userModeChanged();
92102 void sessionModeChanged();
93103 void numSessionsChanged();
@@ -101,6 +111,8 @@ private:
101111 QString m_selectUserHint;
102112 bool m_selectGuestHint;
103113 bool m_hasGuestAccountHint;
114+ bool m_showManualLoginHint;
115+ bool m_hideUsersHint;
104116 QString m_userMode;
105117 QString m_sessionMode;
106118 QList<SessionItem> m_fullSessions;
tests/mocks/liblightdm/MockGreeter.cpp+15−3
@@ -67,17 +67,17 @@ QString Greeter::defaultSessionHint() const
6767
6868bool Greeter::hideUsersHint() const
6969{
70− return false;
70+ return MockController::instance()->hideUsersHint();
7171}
7272
7373bool Greeter::showManualLoginHint() const
7474{
75− return true;
75+ return MockController::instance()->showManualLoginHint();
7676}
7777
7878bool Greeter::showRemoteLoginHint() const
7979{
80− return true;
80+ return false;
8181}
8282
8383QString Greeter::selectUserHint() const
@@ -141,6 +141,11 @@ void Greeter::handleAuthenticate()
141141{
142142 Q_D(Greeter);
143143
144+ if (d->authenticationUser.isEmpty()) {
145+ Q_EMIT showPrompt("Username: ", Greeter::PromptTypeQuestion);
146+ return;
147+ }
148+
144149 // Send out any messages we need to
145150 if (d->authenticationUser == "info-prompt")
146151 Q_EMIT showMessage("Welcome to Unity Greeter", Greeter::MessageTypeInfo);
@@ -219,6 +224,13 @@ void Greeter::respond(const QString &response)
219224{
220225 Q_D(Greeter);
221226
227+ if (d->authenticationUser.isEmpty()) {
228+ // A manual login, our first question was which username
229+ d->authenticationUser = response;
230+ handleAuthenticate();
231+ return;
232+ }
233+
222234 if (d->authenticationUser == "no-response")
223235 return;
224236 else if (d->authenticationUser == "two-factor") {
tests/mocks/liblightdm/MockUsersModel.cpp+4
@@ -67,6 +67,10 @@ UsersModel::UsersModel(QObject *parent)
6767
6868 connect(MockController::instance(), &MockController::hasGuestAccountHintChanged,
6969 this, &UsersModel::resetEntries);
70+ connect(MockController::instance(), &MockController::showManualLoginHintChanged,
71+ this, &UsersModel::resetEntries);
72+ connect(MockController::instance(), &MockController::hideUsersHintChanged,
73+ this, &UsersModel::resetEntries);
7074 connect(MockController::instance(), &MockController::userModeChanged,
7175 this, &UsersModel::resetEntries);
7276 connect(MockController::instance(), &MockController::sessionNameChanged,
tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+53−2
@@ -79,11 +79,9 @@ private Q_SLOTS:
7979 QCOMPARE(findName(model, QStringLiteral("*guest")), -1);
8080
8181 QLightDM::MockController::instance()->setHasGuestAccountHint(true);
82− recreateModel();
8382
8483 int i = findName(model, QStringLiteral("*guest"));
8584 QVERIFY(i >= 0);
86− QCOMPARE(i, model->count() - 1);
8785
8886 auto realName = model->data(i, QLightDM::UsersModel::RealNameRole).toString();
8987 QCOMPARE(realName, QStringLiteral("Guest Session"));
@@ -95,6 +93,59 @@ private Q_SLOTS:
9593 QCOMPARE(session, Greeter::instance()->defaultSessionHint());
9694 }
9795
96+ void testHasManual()
97+ {
98+ // sanity check that it doesn't start already present
99+ QCOMPARE(findName(model, QStringLiteral("*other")), -1);
100+
101+ QLightDM::MockController::instance()->setShowManualLoginHint(true);
102+
103+ int i = findName(model, QStringLiteral("*other"));
104+ QVERIFY(i >= 0);
105+
106+ auto realName = model->data(i, QLightDM::UsersModel::RealNameRole).toString();
107+ QCOMPARE(realName, QStringLiteral("Login"));
108+
109+ auto loggedIn = model->data(i, QLightDM::UsersModel::LoggedInRole).toBool();
110+ QVERIFY(!loggedIn);
111+
112+ auto session = model->data(i, QLightDM::UsersModel::SessionRole).toString();
113+ QCOMPARE(session, Greeter::instance()->defaultSessionHint());
114+ }
115+
116+ void testHideUsers()
117+ {
118+ QLightDM::MockController::instance()->setHideUsersHint(true);
119+
120+ QCOMPARE(model->count(), 1);
121+ auto name = model->data(0, QLightDM::UsersModel::NameRole).toString();
122+ QCOMPARE(name, QStringLiteral("*other"));
123+ }
124+
125+ void testHideUsersWithGuest()
126+ {
127+ QLightDM::MockController::instance()->setHideUsersHint(true);
128+ QLightDM::MockController::instance()->setHasGuestAccountHint(true);
129+
130+ QCOMPARE(model->count(), 1);
131+ auto name = model->data(0, QLightDM::UsersModel::NameRole).toString();
132+ QCOMPARE(name, QStringLiteral("*guest"));
133+ }
134+
135+ void testCustomEntriesAreLast()
136+ {
137+ QLightDM::MockController::instance()->setHasGuestAccountHint(true);
138+ QLightDM::MockController::instance()->setShowManualLoginHint(true);
139+
140+ QVERIFY(model->count() > 2);
141+
142+ int manualIndex = findName(model, QStringLiteral("*other"));
143+ QCOMPARE(manualIndex, model->count() - 2);
144+
145+ int guestIndex = findName(model, QStringLiteral("*guest"));
146+ QCOMPARE(guestIndex, model->count() - 1);
147+ }
148+
98149private:
99150 UsersModel *model = nullptr;
100151};
tests/qmltests/Greeter/tst_Greeter.qml+9−1
@@ -201,7 +201,7 @@ Item {
201201 var i = getIndexOf(name);
202202 compare(view.currentIndex, i);
203203 compare(AccountsService.user, name);
204− if (name === "*guest") // guest has no authenticationUser set
204+ if (name[0] === "*") // custom rows have no authenticationUser set
205205 compare(LightDM.Greeter.authenticationUser, "");
206206 else
207207 compare(LightDM.Greeter.authenticationUser, name);
@@ -612,6 +612,14 @@ Item {
612612 verify(!view.locked);
613613 }
614614
615+ function test_showManualLoginHint() {
616+ LightDMController.showManualLoginHint = true;
617+ resetLoader();
618+ var i = selectUser("*other");
619+ compare(i, LightDM.Users.count - 1); // manual should be last
620+ verify(view.locked);
621+ }
622+
615623 function test_fingerprintSuccess() {
616624 var index = selectUser("has-password");
617625 unlockAndShowGreeter(); // turn on lockscreen mode
tests/qmltests/tst_Shell.qml+29−1
@@ -654,7 +654,8 @@ Rectangle {
654654 // Shell instance gets destroyed.
655655 tryCompare(shellLoader, "itemDestroyed", true);
656656
657− setLightDMMockMode("single"); // back to the default value
657+ LightDMController.reset();
658+ setLightDMMockMode("single"); // these tests default to "single"
658659
659660 AccountsService.demoEdges = false;
660661 AccountsService.demoEdgesCompleted = [];
@@ -1477,6 +1478,33 @@ Rectangle {
14771478 }
14781479 }
14791480
1481+ function test_manualLoginFlow() {
1482+ LightDMController.showManualLoginHint = true;
1483+ setLightDMMockMode("full");
1484+ loadShell("desktop");
1485+
1486+ var i = selectUser("*other");
1487+ var greeter = findChild(shell, "greeter");
1488+ var username = findChild(greeter, "username" + i);
1489+ var promptField = findChild(greeter, "promptField");
1490+ var promptHint = findChild(greeter, "promptHint");
1491+
1492+ compare(username.text, "Login");
1493+ compare(promptHint.text, "Username");
1494+
1495+ tryCompare(promptField, "activeFocus", true);
1496+ typeString("has-password");
1497+ keyClick(Qt.Key_Enter);
1498+
1499+ tryCompare(username, "text", "has-password");
1500+ tryCompare(promptHint, "text", "Passphrase");
1501+
1502+ typeString("password");
1503+ keyClick(Qt.Key_Enter);
1504+
1505+ confirmLoggedIn(true);
1506+ }
1507+
14801508 function test_launcherInverted_data() {
14811509 return [
14821510 {tag: "phone", formFactor: "phone", usageScenario: "phone", launcherInverted: true},