Commit 5e960b15 authored by Renato Silva's avatar Renato Silva Committed by Commit Bot

CrOS - Login Screen - Minor cleanup

Rename AnimationState to UiState and use it to determine the
visibility of elements. Remove multiple variables in favor of
UiState.

Bug: 1075994
Change-Id: I0d7413fb173dadbff22cde0eb3af6f43586bdee2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2352883
Commit-Queue: Renato Silva <rrsilva@google.com>
Reviewed-by: default avatarDenis Kuznetsov [CET] <antrim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798780}
parent 3906443e
......@@ -1774,7 +1774,7 @@ void LockContentsView::LayoutAuth(LoginBigUserView* to_update,
DCHECK(to_update);
auto capture_animation_state_pre_layout = [&](LoginBigUserView* view) {
if (!animate || !view)
if (!view)
return;
if (view->auth_user())
view->auth_user()->CaptureStateForAnimationPreLayout();
......@@ -1829,10 +1829,10 @@ void LockContentsView::LayoutAuth(LoginBigUserView* to_update,
};
auto apply_animation_post_layout = [&](LoginBigUserView* view) {
if (!animate || !view)
if (!view)
return;
if (view->auth_user())
view->auth_user()->ApplyAnimationPostLayout();
view->auth_user()->ApplyAnimationPostLayout(animate);
};
// The high-level layout flow:
......
......@@ -71,13 +71,12 @@ constexpr const char kLoginAuthUserViewClassName[] = "LoginAuthUserView";
// Distance between the user view (ie, the icon and name) and the password
// textfield.
const int kDistanceBetweenUserViewAndPasswordDp = 24;
const int kDistanceBetweenUserViewAndOnlineSigninDp = 24;
const int kDistanceBetweenUserViewAndChallengeResponseDp = 32;
// Distance between the password textfield and the the pin keyboard.
const int kDistanceBetweenPasswordFieldAndPinKeyboardDp = 16;
// The height of the password field.
const int kPasswordFieldHeight = 37;
// Distance from the end of pin keyboard to the bottom of the big user view.
const int kDistanceFromPinKeyboardToBigUserViewBottomDp = 50;
......@@ -837,24 +836,30 @@ class LoginAuthUserView::LockedTpmMessageView : public views::View {
views::ImageView* message_icon_;
};
struct LoginAuthUserView::AnimationState {
explicit AnimationState(LoginAuthUserView* view) {
struct LoginAuthUserView::UiState {
explicit UiState(const LoginAuthUserView* view) {
has_password = view->ShouldShowPasswordField();
has_pinpad = view->ShouldShowPinPad();
has_fingerprint = view->HasAuthMethod(LoginAuthUserView::AUTH_FINGERPRINT);
has_challenge_response =
view->HasAuthMethod(LoginAuthUserView::AUTH_CHALLENGE_RESPONSE);
auth_disabled = view->HasAuthMethod(LoginAuthUserView::AUTH_DISABLED);
force_online_sign_in =
view->HasAuthMethod(LoginAuthUserView::AUTH_ONLINE_SIGN_IN);
non_pin_y_start_in_screen = view->GetBoundsInScreen().y();
pin_start_in_screen = view->pin_view_->GetBoundsInScreen().origin();
had_pinpad = view->ShouldShowPinPad();
had_password = view->ShouldShowPasswordField();
had_fingerprint = view->HasAuthMethod(LoginAuthUserView::AUTH_FINGERPRINT);
had_challenge_response =
view->HasAuthMethod(LoginAuthUserView::AUTH_CHALLENGE_RESPONSE);
}
bool has_password = false;
bool has_pinpad = false;
bool has_fingerprint = false;
bool has_challenge_response = false;
bool auth_disabled = false;
bool force_online_sign_in = false;
// Used for this view's animation in `ApplyAnimationPostLayout`.
int non_pin_y_start_in_screen = 0;
gfx::Point pin_start_in_screen;
bool had_pinpad = false;
bool had_password = false;
bool had_fingerprint = false;
bool had_challenge_response = false;
};
LoginAuthUserView::TestApi::TestApi(LoginAuthUserView* view) : view_(view) {}
......@@ -1072,7 +1077,9 @@ LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user,
add_padding(kDistanceFromPinKeyboardToBigUserViewBottomDp);
// Update authentication UI.
CaptureStateForAnimationPreLayout();
SetAuthMethods(auth_methods_);
ApplyAnimationPostLayout(true);
user_view_->UpdateForUser(user, false /*animate*/);
}
......@@ -1080,51 +1087,38 @@ LoginAuthUserView::~LoginAuthUserView() = default;
void LoginAuthUserView::SetAuthMethods(uint32_t auth_methods,
AuthMethodsMetadata auth_metadata) {
bool had_password = ShouldShowPasswordField();
// It is an error to call this method without storing the previous state.
DCHECK(previous_state_);
// Apply changes and determine the new state of input fields.
auth_methods_ = static_cast<AuthMethods>(auth_methods);
auth_metadata_ = auth_metadata;
UpdateInputFieldMode();
const UiState current_state{this};
bool has_password = ShouldShowPasswordField();
bool has_pinpad = ShouldShowPinPad();
bool force_online_sign_in = HasAuthMethod(AUTH_ONLINE_SIGN_IN);
bool has_fingerprint = HasAuthMethod(AUTH_FINGERPRINT);
bool has_challenge_response = HasAuthMethod(AUTH_CHALLENGE_RESPONSE);
bool auth_disabled = HasAuthMethod(AUTH_DISABLED);
bool hide_auth = auth_disabled || force_online_sign_in || tpm_is_locked_;
online_sign_in_message_->SetVisible(force_online_sign_in);
disabled_auth_message_->SetVisible(auth_disabled);
if (auth_disabled && !tpm_is_locked_)
disabled_auth_message_->RequestFocus();
online_sign_in_message_->SetVisible(current_state.force_online_sign_in);
disabled_auth_message_->SetVisible(current_state.auth_disabled);
locked_tpm_message_view_->SetVisible(tpm_is_locked_);
if (tpm_is_locked_)
locked_tpm_message_view_->RequestFocus();
// Adjust the PIN keyboard visibility before the password textfield's one, so
// that when both are about to be hidden the focus doesn't jump to the "1"
// keyboard button, causing unexpected accessibility effects.
pin_view_->SetVisible(has_pinpad);
pin_view_->SetVisible(current_state.has_pinpad);
password_view_->SetEnabled(has_password);
password_view_->SetEnabled(current_state.has_password);
password_view_->SetEnabledOnEmptyPassword(HasAuthMethod(AUTH_TAP));
password_view_->SetFocusEnabledForChildViews(has_password);
password_view_->SetVisible(!hide_auth && has_password);
password_view_->layer()->SetOpacity(has_password ? 1 : 0);
password_view_->SetFocusEnabledForChildViews(current_state.has_password);
password_view_->SetVisible(current_state.has_password);
password_view_->layer()->SetOpacity(current_state.has_password ? 1 : 0);
if (!had_password && has_password)
password_view_->RequestFocus();
fingerprint_view_->SetVisible(has_fingerprint);
fingerprint_view_->SetVisible(current_state.has_fingerprint);
fingerprint_view_->SetCanUsePin(HasAuthMethod(AUTH_PIN));
challenge_response_view_->SetVisible(has_challenge_response);
challenge_response_view_->SetVisible(current_state.has_challenge_response);
padding_below_user_view_->SetPreferredSize(GetPaddingBelowUserView());
padding_below_password_view_->SetPreferredSize(GetPaddingBelowPasswordView());
padding_below_user_view_->SetPreferredSize(
gfx::Size(kNonEmptyWidthDp, CalcPaddingHeightBelowUserView()));
padding_below_password_view_->SetPreferredSize(
gfx::Size(kNonEmptyWidthDp, CalcPaddingHeightBelowPasswordView()));
password_view_->SetPlaceholderText(GetPasswordViewPlaceholder());
const std::string& user_display_email =
......@@ -1133,19 +1127,13 @@ void LoginAuthUserView::SetAuthMethods(uint32_t auth_methods,
IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME,
base::UTF8ToUTF16(user_display_email)));
// Only the active auth user view has a password displayed. If that is the
// Only the active auth user view has authentication methods. If that is the
// case, then render the user view as if it was always focused, since clicking
// on it will not do anything (such as swapping users).
user_view_->SetForceOpaque(auth_methods_ != AUTH_NONE);
user_view_->SetTapEnabled(!HasAuthMethod(AUTH_PASSWORD));
// Tapping the user view will trigger the online sign-in flow when
// |force_online_sign_in| is true.
if (force_online_sign_in)
user_view_->RequestFocus();
if (has_challenge_response)
challenge_response_view_->RequestFocus();
user_view_->SetTapEnabled(auth_methods_ == AUTH_NONE);
UpdateFocus();
PreferredSizeChanged();
}
......@@ -1180,18 +1168,19 @@ void LoginAuthUserView::CaptureStateForAnimationPreLayout() {
stop_animation(fingerprint_view_);
stop_animation(challenge_response_view_);
DCHECK(!cached_animation_state_);
cached_animation_state_ = std::make_unique<AnimationState>(this);
DCHECK(!previous_state_);
previous_state_ = std::make_unique<UiState>(this);
}
void LoginAuthUserView::ApplyAnimationPostLayout() {
DCHECK(cached_animation_state_);
void LoginAuthUserView::ApplyAnimationPostLayout(bool animate) {
DCHECK(previous_state_);
// Release the previous state if no animation should be performed.
if (!animate) {
previous_state_.reset();
return;
}
bool has_password = ShouldShowPasswordField();
bool had_password = cached_animation_state_->had_password;
bool has_pinpad = ShouldShowPinPad();
bool has_fingerprint = HasAuthMethod(AUTH_FINGERPRINT);
bool has_challenge_response = HasAuthMethod(AUTH_CHALLENGE_RESPONSE);
const UiState current_state{this};
////////
// Animate the user info (ie, icon, name) up or down the screen.
......@@ -1204,7 +1193,7 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
// but it seems that the timing gets slightly out of sync with the PIN
// animation.
auto move_to_center = std::make_unique<ui::InterpolatedTranslation>(
gfx::PointF(0, cached_animation_state_->non_pin_y_start_in_screen -
gfx::PointF(0, previous_state_->non_pin_y_start_in_screen -
non_pin_y_end_in_screen),
gfx::PointF());
auto transition =
......@@ -1223,9 +1212,9 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
////////
// Fade the password view if it is being hidden or shown.
if (had_password != has_password) {
if (current_state.has_password != previous_state_->has_password) {
float opacity_start = 0, opacity_end = 1;
if (!has_password)
if (!current_state.has_password)
std::swap(opacity_start, opacity_end);
password_view_->layer()->SetOpacity(opacity_start);
......@@ -1236,7 +1225,7 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
login_constants::kChangeUserAnimationDurationMs));
settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN);
if (had_password && !has_password) {
if (previous_state_->has_password && !current_state.has_password) {
settings.AddObserver(
new ClearPasswordAndHideAnimationObserver(password_view_));
}
......@@ -1248,13 +1237,13 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
////////
// Grow/shrink the PIN keyboard if it is being hidden or shown.
if (cached_animation_state_->had_pinpad != has_pinpad) {
if (!has_pinpad) {
if (previous_state_->has_pinpad != current_state.has_pinpad) {
if (!current_state.has_pinpad) {
gfx::Point pin_end_in_screen = pin_view_->GetBoundsInScreen().origin();
gfx::Rect pin_bounds = pin_view_->bounds();
pin_bounds.set_x(cached_animation_state_->pin_start_in_screen.x() -
pin_bounds.set_x(previous_state_->pin_start_in_screen.x() -
pin_end_in_screen.x());
pin_bounds.set_y(cached_animation_state_->pin_start_in_screen.y() -
pin_bounds.set_y(previous_state_->pin_start_in_screen.y() -
pin_end_in_screen.y());
// Since PIN is disabled, the previous Layout() hid the PIN keyboard.
......@@ -1264,7 +1253,7 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
}
auto transition = std::make_unique<PinKeyboardAnimation>(
has_pinpad /*grow*/, pin_view_->height(),
current_state.has_pinpad /*grow*/, pin_view_->height(),
// TODO(https://crbug.com/955119): Implement proper animation.
base::TimeDelta::FromMilliseconds(
login_constants::kChangeUserAnimationDurationMs / 2.0f),
......@@ -1272,7 +1261,7 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
auto* sequence = new ui::LayerAnimationSequence(std::move(transition));
// Hide the PIN keyboard after animation if needed.
if (!has_pinpad) {
if (!current_state.has_pinpad) {
auto* observer = BuildObserverToHideView(pin_view_);
sequence->AddObserver(observer);
observer->SetActive();
......@@ -1286,9 +1275,9 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
////////
// Fade the fingerprint view if it is being hidden or shown.
if (cached_animation_state_->had_fingerprint != has_fingerprint) {
if (previous_state_->has_fingerprint != current_state.has_fingerprint) {
float opacity_start = 0, opacity_end = 1;
if (!has_fingerprint)
if (!current_state.has_fingerprint)
std::swap(opacity_start, opacity_end);
fingerprint_view_->layer()->SetOpacity(opacity_start);
......@@ -1305,10 +1294,10 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
////////
// Fade the challenge response (Smart Card) if it is being hidden or shown.
if (cached_animation_state_->had_challenge_response !=
has_challenge_response) {
if (previous_state_->has_challenge_response !=
current_state.has_challenge_response) {
float opacity_start = 0, opacity_end = 1;
if (!has_challenge_response)
if (!current_state.has_challenge_response)
std::swap(opacity_start, opacity_end);
challenge_response_view_->layer()->SetOpacity(opacity_start);
......@@ -1323,7 +1312,7 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
}
}
cached_animation_state_.reset();
previous_state_.reset();
}
void LoginAuthUserView::UpdateForUser(const LoginUserInfo& user) {
......@@ -1360,7 +1349,10 @@ void LoginAuthUserView::SetTpmLockedState(bool is_locked,
locked_tpm_message_view_->SetRemainingTime(time_left);
tpm_is_locked_ = is_locked;
// Update auth methods which are available.
CaptureStateForAnimationPreLayout();
SetAuthMethods(auth_methods_, auth_metadata_);
Layout();
ApplyAnimationPostLayout(true);
}
const LoginUserInfo& LoginAuthUserView::current_user() const {
......@@ -1485,13 +1477,41 @@ void LoginAuthUserView::AttemptAuthenticateWithChallengeResponse() {
weak_factory_.GetWeakPtr()));
}
void LoginAuthUserView::UpdateFocus() {
DCHECK(previous_state_);
const UiState current_state{this};
// All states are exclusive.
if (current_state.auth_disabled)
disabled_auth_message_->RequestFocus();
if (tpm_is_locked_)
locked_tpm_message_view_->RequestFocus();
if (current_state.has_challenge_response)
challenge_response_view_->RequestFocus();
if (current_state.has_password && !previous_state_->has_password)
password_view_->RequestFocus();
// Tapping the user view will trigger the online sign-in flow when
// |force_online_sign_in| is true.
if (current_state.force_online_sign_in)
user_view_->RequestFocus();
}
void LoginAuthUserView::UpdateInputFieldMode() {
// Currently the challenge-response authentication can't be combined
// with the password or PIN based one.
if (!HasAuthMethod(AUTH_PASSWORD) || HasAuthMethod(AUTH_CHALLENGE_RESPONSE)) {
input_field_mode_ = InputFieldMode::DISABLED;
// There isn't an input field when any of the following is true:
// - Challenge response is active (Smart Card)
// - Online sign in message shown
// - Disabled message shown
// - TPM locked
// - No password auth available
if (HasAuthMethod(AUTH_CHALLENGE_RESPONSE) ||
HasAuthMethod(AUTH_ONLINE_SIGN_IN) ||
HasAuthMethod(AUTH_DISABLED) ||
tpm_is_locked_ ||
!HasAuthMethod(AUTH_PASSWORD)) {
input_field_mode_ = InputFieldMode::NONE;
return;
}
if (!HasAuthMethod(AUTH_PIN)) {
input_field_mode_ = InputFieldMode::PASSWORD_ONLY;
return;
......@@ -1505,7 +1525,7 @@ bool LoginAuthUserView::ShouldShowPinPad() const {
if (auth_metadata_.virtual_keyboard_visible)
return false;
switch (input_field_mode_) {
case InputFieldMode::DISABLED:
case InputFieldMode::NONE:
return false;
case InputFieldMode::PASSWORD_ONLY:
return auth_metadata_.show_pinpad_for_pw;
......@@ -1519,26 +1539,30 @@ bool LoginAuthUserView::ShouldShowPasswordField() const {
input_field_mode_ == InputFieldMode::PIN_AND_PASSWORD;
}
gfx::Size LoginAuthUserView::GetPaddingBelowUserView() const {
int height = kDistanceBetweenUserViewAndPasswordDp;
int LoginAuthUserView::CalcPaddingHeightBelowUserView() const {
const UiState state{this};
// Compensate with the height of the password field if there isn't
// an input field or smart card login (challenge response).
if (!ShouldShowPasswordField() && !HasAuthMethod(AUTH_CHALLENGE_RESPONSE))
height += kPasswordFieldHeight;
if (state.has_password)
return kDistanceBetweenUserViewAndPasswordDp;
if (state.force_online_sign_in)
return kDistanceBetweenUserViewAndOnlineSigninDp;
if (state.has_challenge_response)
return kDistanceBetweenUserViewAndChallengeResponseDp;
return gfx::Size(kNonEmptyWidthDp, height);
return 0;
}
gfx::Size LoginAuthUserView::GetPaddingBelowPasswordView() const {
int padding_view_height = kDistanceBetweenPasswordFieldAndPinKeyboardDp;
if (HasAuthMethod(AUTH_FINGERPRINT) && !ShouldShowPinPad()) {
padding_view_height = kDistanceBetweenPasswordFieldAndFingerprintViewDp;
} else if (HasAuthMethod(AUTH_CHALLENGE_RESPONSE) && !ShouldShowPinPad()) {
padding_view_height =
kDistanceBetweenPasswordFieldAndChallengeResponseViewDp;
}
return gfx::Size(kNonEmptyWidthDp, padding_view_height);
int LoginAuthUserView::CalcPaddingHeightBelowPasswordView() const {
const UiState state{this};
if (state.has_pinpad)
return kDistanceBetweenPasswordFieldAndPinKeyboardDp;
if (state.has_fingerprint)
return kDistanceBetweenPasswordFieldAndFingerprintViewDp;
if (state.has_challenge_response)
return kDistanceBetweenPasswordFieldAndChallengeResponseViewDp;
return 0;
}
base::string16 LoginAuthUserView::GetPasswordViewPlaceholder() const {
......
......@@ -69,7 +69,7 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
// This is determined by the current authentication methods
// that a user has.
enum class InputFieldMode {
DISABLED, // Not showing any input field.
NONE, // Not showing any input field.
PASSWORD_ONLY, // No PIN set. Password only field.
PIN_AND_PASSWORD, // PIN set.
};
......@@ -126,7 +126,8 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
// Set the displayed set of auth methods. |auth_methods| contains or-ed
// together AuthMethod values. |auth_metadata| provides additional control
// parameters for the view.
// parameters for the view. Must always be called in conjunction with
// `CaptureStateForAnimationPreLayout` and `ApplyAnimationPostLayout`.
void SetAuthMethods(
uint32_t auth_methods,
AuthMethodsMetadata auth_metadata = AuthMethodsMetadata());
......@@ -141,8 +142,9 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
// animation.
void CaptureStateForAnimationPreLayout();
// Applies animation based on current layout state compared to the most
// recently captured state.
void ApplyAnimationPostLayout();
// recently captured state. If `animate` is false, the previous UI state
// is released and no animation is performed.
void ApplyAnimationPostLayout(bool animate);
// Update the displayed name, icon, etc to that of |user|.
void UpdateForUser(const LoginUserInfo& user);
......@@ -175,7 +177,7 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
private:
struct AnimationState;
struct UiState;
class FingerprintView;
class ChallengeResponseView;
class DisabledAuthMessageView;
......@@ -214,6 +216,9 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
// starts the asynchronous authentication process against a security token.
void AttemptAuthenticateWithChallengeResponse();
// Updates the element in focus. Used in `ApplyAnimationPostLayout`.
void UpdateFocus();
// Determines the mode of the input field based on the available
// authentication methods.
void UpdateInputFieldMode();
......@@ -223,8 +228,8 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
bool ShouldShowPasswordField() const;
// Convenience methods to determine the necessary paddings.
gfx::Size GetPaddingBelowUserView() const;
gfx::Size GetPaddingBelowPasswordView() const;
int CalcPaddingHeightBelowUserView() const;
int CalcPaddingHeightBelowPasswordView() const;
// Convenience methods to determine UI text based on the InputFieldMode.
base::string16 GetPasswordViewPlaceholder() const;
......@@ -234,7 +239,7 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
AuthMethodsMetadata auth_metadata_ = AuthMethodsMetadata();
// Controls which input field is currently being shown.
InputFieldMode input_field_mode_ = InputFieldMode::DISABLED;
InputFieldMode input_field_mode_ = InputFieldMode::NONE;
LoginUserView* user_view_ = nullptr;
LoginPasswordView* password_view_ = nullptr;
......@@ -259,10 +264,10 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView,
bool tpm_is_locked_ = false;
// Animation state that was cached from before a layout. Generated by
// |CaptureStateForAnimationPreLayout| and consumed by
// |ApplyAnimationPostLayout|.
std::unique_ptr<AnimationState> cached_animation_state_;
// UI state that was stored before setting new authentication methods.
// Generated by `CaptureStateForAnimationPreLayout` and consumed by
// `ApplyAnimationPostLayout`.
std::unique_ptr<UiState> previous_state_;
base::WeakPtrFactory<LoginAuthUserView> weak_factory_{this};
......
......@@ -65,7 +65,9 @@ class LoginAuthUserViewUnittest : public LoginTestBase {
auth_metadata.show_pinpad_for_pw = show_pinpad_for_pw;
auth_metadata.virtual_keyboard_visible = virtual_keyboard_visible;
auth_metadata.autosubmit_pin_length = autosubmit_pin_length;
view_->CaptureStateForAnimationPreLayout();
view_->SetAuthMethods(auth_methods, auth_metadata);
view_->ApplyAnimationPostLayout(true);
}
LoginUserInfo user_;
......@@ -183,18 +185,14 @@ TEST_F(LoginAuthUserViewUnittest,
password_test.textfield()->SetText(base::ASCIIToUTF16("Hello"));
// Enable some other auth method (PIN), password is not cleared.
view_->CaptureStateForAnimationPreLayout();
EXPECT_TRUE(has_password());
SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD |
LoginAuthUserView::AUTH_PIN);
EXPECT_TRUE(has_password());
view_->ApplyAnimationPostLayout();
EXPECT_TRUE(has_password());
// Disable password, password is cleared.
view_->CaptureStateForAnimationPreLayout();
SetAuthMethods(LoginAuthUserView::AUTH_NONE);
EXPECT_TRUE(has_password());
view_->ApplyAnimationPostLayout();
SetAuthMethods(LoginAuthUserView::AUTH_NONE);
EXPECT_FALSE(has_password());
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment