From 99f965ae259782115b728509d882fb4edb5027ca Mon Sep 17 00:00:00 2001 From: Amir Zarrinkafsh Date: Wed, 7 Dec 2022 20:22:03 +1100 Subject: [PATCH] test(suites): refactor flaky tests (#4502) --- cmd/authelia-scripts/cmd/bootstrap.go | 6 ++--- internal/suites/action_login.go | 24 +++++++++++++++---- internal/suites/action_reset_password.go | 18 ++++++++++---- internal/suites/action_totp.go | 4 +++- .../authelia/docker-compose.frontend.dev.yml | 2 +- .../scenario_default_redirection_url_test.go | 16 ++++++++++--- internal/suites/scenario_inactivity_test.go | 1 + internal/suites/scenario_oidc_test.go | 1 + internal/suites/scenario_two_factor_test.go | 19 +++++++++++---- 9 files changed, 70 insertions(+), 21 deletions(-) diff --git a/cmd/authelia-scripts/cmd/bootstrap.go b/cmd/authelia-scripts/cmd/bootstrap.go index ce2583e6c..eb511639f 100644 --- a/cmd/authelia-scripts/cmd/bootstrap.go +++ b/cmd/authelia-scripts/cmd/bootstrap.go @@ -151,8 +151,8 @@ func createTemporaryDirectory() { func createPNPMDirectory() { home := os.Getenv("HOME") if home != "" { - bootstrapPrintln("Creating ", home+"/.pnpm-store") - err := os.MkdirAll(home+"/.pnpm-store", 0755) + bootstrapPrintln("Creating ", home+"/.local/share/pnpm/store") + err := os.MkdirAll(home+"/.local/share/pnpm/store", 0755) if err != nil { panic(err) @@ -161,7 +161,7 @@ func createPNPMDirectory() { } func pnpmInstall() { - bootstrapPrintln("Installing web dependences ") + bootstrapPrintln("Installing web dependencies ") cwd, err := os.Getwd() if err != nil { diff --git a/internal/suites/action_login.go b/internal/suites/action_login.go index cb327b2d4..9eaf193a7 100644 --- a/internal/suites/action_login.go +++ b/internal/suites/action_login.go @@ -10,22 +10,38 @@ import ( func (rs *RodSession) doFillLoginPageAndClick(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool) { usernameElement := rs.WaitElementLocatedByID(t, page, "username-textfield") - err := usernameElement.Input(username) + passwordElement := rs.WaitElementLocatedByID(t, page, "password-textfield") + buttonElement := rs.WaitElementLocatedByID(t, page, "sign-in-button") + +username: + err := usernameElement.MustSelectAllText().Input(username) require.NoError(t, err) - passwordElement := rs.WaitElementLocatedByID(t, page, "password-textfield") - err = passwordElement.Input(password) + if usernameElement.MustText() != username { + goto username + } + +password: + err = passwordElement.MustSelectAllText().Input(password) require.NoError(t, err) + if passwordElement.MustText() != password { + goto password + } + if keepMeLoggedIn { keepMeLoggedInElement := rs.WaitElementLocatedByID(t, page, "remember-checkbox") err = keepMeLoggedInElement.Click("left", 1) require.NoError(t, err) } - buttonElement := rs.WaitElementLocatedByID(t, page, "sign-in-button") +click: err = buttonElement.Click("left", 1) require.NoError(t, err) + + if buttonElement.MustInteractable() { + goto click + } } // Login 1FA. diff --git a/internal/suites/action_reset_password.go b/internal/suites/action_reset_password.go index 112771ed4..8980ceb70 100644 --- a/internal/suites/action_reset_password.go +++ b/internal/suites/action_reset_password.go @@ -2,7 +2,6 @@ package suites import ( "testing" - "time" "github.com/go-rod/rod" "github.com/stretchr/testify/require" @@ -23,16 +22,25 @@ func (rs *RodSession) doCompletePasswordReset(t *testing.T, page *rod.Page, newP link := doGetLinkFromLastMail(t) rs.doVisit(t, page, link) - time.Sleep(1 * time.Second) + password1 := rs.WaitElementLocatedByID(t, page, "password1-textfield") + password2 := rs.WaitElementLocatedByID(t, page, "password2-textfield") - err := rs.WaitElementLocatedByID(t, page, "password1-textfield").Input(newPassword1) +password1: + err := password1.MustSelectAllText().Input(newPassword1) require.NoError(t, err) - time.Sleep(1 * time.Second) + if password1.MustText() != newPassword1 { + goto password1 + } - err = rs.WaitElementLocatedByID(t, page, "password2-textfield").Input(newPassword2) +password2: + err = password2.MustSelectAllText().Input(newPassword2) require.NoError(t, err) + if password2.MustText() != newPassword2 { + goto password2 + } + err = rs.WaitElementLocatedByID(t, page, "reset-button").Click("left", 1) require.NoError(t, err) } diff --git a/internal/suites/action_totp.go b/internal/suites/action_totp.go index 78636eafb..10fdcaaa4 100644 --- a/internal/suites/action_totp.go +++ b/internal/suites/action_totp.go @@ -6,6 +6,7 @@ import ( "time" "github.com/go-rod/rod" + "github.com/go-rod/rod/lib/input" "github.com/pquerna/otp/totp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -31,7 +32,8 @@ func (rs *RodSession) doEnterOTP(t *testing.T, page *rod.Page, code string) { inputs := rs.WaitElementsLocatedByID(t, page, "otp-input input") for i := 0; i < len(code); i++ { - inputs[i].MustInput(string(code[i])) + err := inputs[i].Type(input.Key(code[i])) + require.NoError(t, err) } } diff --git a/internal/suites/example/compose/authelia/docker-compose.frontend.dev.yml b/internal/suites/example/compose/authelia/docker-compose.frontend.dev.yml index 176f1dc1d..cc7056037 100644 --- a/internal/suites/example/compose/authelia/docker-compose.frontend.dev.yml +++ b/internal/suites/example/compose/authelia/docker-compose.frontend.dev.yml @@ -14,7 +14,7 @@ services: volumes: - './example/compose/authelia/resources/:/resources' - '../../web:/app' - - '~/.pnpm-store:/tmp/.pnpm-store' + - '~/.local/share/pnpm/store:/tmp/.pnpm-store' labels: # Traefik 1.x - 'traefik.frontend.rule=Host:login.example.com' diff --git a/internal/suites/scenario_default_redirection_url_test.go b/internal/suites/scenario_default_redirection_url_test.go index 6dd521309..31fca33d0 100644 --- a/internal/suites/scenario_default_redirection_url_test.go +++ b/internal/suites/scenario_default_redirection_url_test.go @@ -30,6 +30,18 @@ func (s *DefaultRedirectionURLScenario) SetupSuite() { } s.RodSession = browser + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer func() { + cancel() + s.collectScreenshot(ctx.Err(), s.Page) + + s.collectCoverage(s.Page) + s.MustClose() + }() + + s.Page = s.doCreateTab(s.T(), HomeBaseURL) + s.secret = s.doLoginAndRegisterTOTP(s.T(), s.Context(ctx), "john", "password", false) } func (s *DefaultRedirectionURLScenario) TearDownSuite() { @@ -59,9 +71,7 @@ func (s *DefaultRedirectionURLScenario) TestUserIsRedirectedToDefaultURL() { targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL) - s.doVisit(s.T(), s.Context(ctx), HomeBaseURL) - s.verifyIsHome(s.T(), s.Page) - s.secret = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, targetURL) + s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, s.secret, targetURL) s.verifySecretAuthorized(s.T(), s.Context(ctx)) s.doLogout(s.T(), s.Context(ctx)) diff --git a/internal/suites/scenario_inactivity_test.go b/internal/suites/scenario_inactivity_test.go index 778b183cd..5f6a4bdfe 100644 --- a/internal/suites/scenario_inactivity_test.go +++ b/internal/suites/scenario_inactivity_test.go @@ -12,6 +12,7 @@ import ( type InactivityScenario struct { *RodSuite + secret string } diff --git a/internal/suites/scenario_oidc_test.go b/internal/suites/scenario_oidc_test.go index 65b867471..a79d9bf59 100644 --- a/internal/suites/scenario_oidc_test.go +++ b/internal/suites/scenario_oidc_test.go @@ -16,6 +16,7 @@ import ( type OIDCScenario struct { *RodSuite + secret string } diff --git a/internal/suites/scenario_two_factor_test.go b/internal/suites/scenario_two_factor_test.go index 7672aab7e..c22c8549b 100644 --- a/internal/suites/scenario_two_factor_test.go +++ b/internal/suites/scenario_two_factor_test.go @@ -12,6 +12,8 @@ import ( type TwoFactorSuite struct { *RodSuite + + secret string } func New2FAScenario() *TwoFactorSuite { @@ -28,6 +30,18 @@ func (s *TwoFactorSuite) SetupSuite() { } s.RodSession = browser + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer func() { + cancel() + s.collectScreenshot(ctx.Err(), s.Page) + + s.collectCoverage(s.Page) + s.MustClose() + }() + + s.Page = s.doCreateTab(s.T(), HomeBaseURL) + s.secret = s.doLoginAndRegisterTOTP(s.T(), s.Context(ctx), "john", "password", false) } func (s *TwoFactorSuite) TearDownSuite() { @@ -60,7 +74,7 @@ func (s *TwoFactorSuite) TestShouldAuthorizeSecretAfterTwoFactor() { // Login and register TOTP, logout and login again with 1FA & 2FA. targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL) - _ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), username, password, false, targetURL) + s.doLoginTwoFactor(s.T(), s.Context(ctx), username, password, false, s.secret, targetURL) // And check if the user is redirected to the secret. s.verifySecretAuthorized(s.T(), s.Context(ctx)) @@ -81,9 +95,6 @@ func (s *TwoFactorSuite) TestShouldFailTwoFactor() { s.collectScreenshot(ctx.Err(), s.Page) }() - // Register TOTP secret and logout. - s.doRegisterThenLogout(s.T(), s.Context(ctx), testUsername, testPassword) - wrongPasscode := "123456" s.doLoginOneFactor(s.T(), s.Context(ctx), testUsername, testPassword, false, "")