summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod2
-rw-r--r--go.sum23
-rw-r--r--internal/suites/action_2fa_methods.go10
-rw-r--r--internal/suites/action_login.go50
-rw-r--r--internal/suites/action_logout.go19
-rw-r--r--internal/suites/action_register.go9
-rw-r--r--internal/suites/action_reset_password.go48
-rw-r--r--internal/suites/action_totp.go25
-rw-r--r--internal/suites/action_visit.go24
-rw-r--r--internal/suites/const.go1
-rw-r--r--internal/suites/example/compose/authelia/resources/reflex.conf2
-rw-r--r--internal/suites/example/compose/haproxy/haproxy.cfg16
-rw-r--r--internal/suites/scenario_available_methods_test.go40
-rw-r--r--internal/suites/scenario_bypass_policy_test.go34
-rw-r--r--internal/suites/scenario_custom_headers_test.go105
-rw-r--r--internal/suites/scenario_default_redirection_url_test.go55
-rw-r--r--internal/suites/scenario_inactivity_test.go88
-rw-r--r--internal/suites/scenario_oidc_test.go99
-rw-r--r--internal/suites/scenario_one_factor_test.go50
-rw-r--r--internal/suites/scenario_password_complexity_test.go36
-rw-r--r--internal/suites/scenario_redirection_check_test.go43
-rw-r--r--internal/suites/scenario_redirection_url_test.go40
-rw-r--r--internal/suites/scenario_regulation_test.go54
-rw-r--r--internal/suites/scenario_reset_password_test.go76
-rw-r--r--internal/suites/scenario_signin_email_test.go32
-rw-r--r--internal/suites/scenario_two_factor_test.go69
-rw-r--r--internal/suites/scenario_user_preferences_test.go76
-rw-r--r--internal/suites/suite_activedirectory_test.go4
-rw-r--r--internal/suites/suite_bypass_all_test.go33
-rw-r--r--internal/suites/suite_docker_test.go4
-rw-r--r--internal/suites/suite_duo_push_test.go88
-rw-r--r--internal/suites/suite_haproxy.go7
-rw-r--r--internal/suites/suite_haproxy_test.go4
-rw-r--r--internal/suites/suite_high_availability_test.go153
-rw-r--r--internal/suites/suite_kubernetes_test.go4
-rw-r--r--internal/suites/suite_ldap_test.go4
-rw-r--r--internal/suites/suite_mariadb_test.go4
-rw-r--r--internal/suites/suite_mysql_test.go4
-rw-r--r--internal/suites/suite_network_acl_test.go36
-rw-r--r--internal/suites/suite_oidc_test.go4
-rw-r--r--internal/suites/suite_oidc_traefik_test.go4
-rw-r--r--internal/suites/suite_one_factor_only_test.go79
-rw-r--r--internal/suites/suite_pathprefix_test.go4
-rw-r--r--internal/suites/suite_postgres_test.go4
-rw-r--r--internal/suites/suite_short_timeouts_test.go4
-rw-r--r--internal/suites/suite_standalone_test.go88
-rw-r--r--internal/suites/suite_traefik2_test.go37
-rw-r--r--internal/suites/suite_traefik_test.go4
-rw-r--r--internal/suites/suites.go14
-rw-r--r--internal/suites/utils.go57
-rw-r--r--internal/suites/verify_body_contains.go34
-rw-r--r--internal/suites/verify_is_authenticated_page.go7
-rw-r--r--internal/suites/verify_is_consent_page.go7
-rw-r--r--internal/suites/verify_is_first_factor_page.go7
-rw-r--r--internal/suites/verify_is_home.go8
-rw-r--r--internal/suites/verify_is_oidc.go12
-rw-r--r--internal/suites/verify_is_public.go13
-rw-r--r--internal/suites/verify_is_second_factor_page.go7
-rw-r--r--internal/suites/verify_mail.go7
-rw-r--r--internal/suites/verify_notification.go8
-rw-r--r--internal/suites/verify_secret_authorized.go7
-rw-r--r--internal/suites/verify_url_is.go18
-rw-r--r--internal/suites/webdriver.go257
-rw-r--r--internal/utils/const.go2
64 files changed, 1138 insertions, 1026 deletions
diff --git a/go.mod b/go.mod
index 007402eda..19eb4b055 100644
--- a/go.mod
+++ b/go.mod
@@ -13,6 +13,7 @@ require (
github.com/fasthttp/router v1.4.4
github.com/fasthttp/session/v2 v2.4.4
github.com/go-ldap/ldap/v3 v3.4.1
+ github.com/go-rod/rod v0.101.8
github.com/go-sql-driver/mysql v1.6.0
github.com/golang-jwt/jwt/v4 v4.1.0
github.com/golang/mock v1.6.0
@@ -30,7 +31,6 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.2.1
github.com/stretchr/testify v1.7.0
- github.com/tebeka/selenium v0.9.9
github.com/tstranex/u2f v1.0.0
github.com/valyala/fasthttp v1.31.0
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b // indirect
diff --git a/go.sum b/go.sum
index a5009bb9b..86a0765c3 100644
--- a/go.sum
+++ b/go.sum
@@ -43,10 +43,7 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e h1:4ZrkT/RzpnROylmoQL57iVUL57wGKTR5O6KpVnbm2tA=
-github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
@@ -89,8 +86,6 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -119,8 +114,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
-github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
-github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmatcuk/doublestar/v2 v2.0.3/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
@@ -315,6 +308,8 @@ github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7
github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
+github.com/go-rod/rod v0.101.8 h1:oV0O97uwjkCVyAP0hD6K6bBE8FUMIjs0dtF7l6kEBsU=
+github.com/go-rod/rod v0.101.8/go.mod h1:N/zlT53CfSpq74nb6rOR0K8UF0SPUPBmzBnArrms+mY=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -646,10 +641,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-github/v27 v27.0.4/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0=
github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -1297,8 +1290,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/tebeka/selenium v0.9.9 h1:cNziB+etNgyH/7KlNI7RMC1ua5aH1+5wUlFQyzeMh+w=
-github.com/tebeka/selenium v0.9.9/go.mod h1:5Fr8+pUvU6B1OiPfkdCKdXZyr5znvVkxuPd0NOdZCQc=
github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4=
github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -1342,6 +1333,16 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
+github.com/ysmood/goob v0.3.0 h1:XZ51cZJ4W3WCoCiUktixzMIQF86W7G5VFL4QQ/Q2uS0=
+github.com/ysmood/goob v0.3.0/go.mod h1:S3lq113Y91y1UBf1wj1pFOxeahvfKkCk6mTWTWbDdWs=
+github.com/ysmood/got v0.15.1 h1:X5jAbMyBf5yeezuFMp9HaMGXZWMSqIQcUlAHI+kJmUs=
+github.com/ysmood/got v0.15.1/go.mod h1:pE1l4LOwOBhQg6A/8IAatkGp7uZjnalzrZolnlhhMgY=
+github.com/ysmood/gotrace v0.2.2 h1:006KHGRThSRf8lwh4EyhNmuuq/l+Ygs+JqojkhEG1/E=
+github.com/ysmood/gotrace v0.2.2/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
+github.com/ysmood/gson v0.6.4 h1:Yb6tosv6bk59HqjZu2/7o4BFherpYEMkDkXmlhgryZ4=
+github.com/ysmood/gson v0.6.4/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
+github.com/ysmood/leakless v0.7.0 h1:XCGdaPExyoreoQd+H5qgxM3ReNbSPFsEXpSKwbXbwQw=
+github.com/ysmood/leakless v0.7.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
diff --git a/internal/suites/action_2fa_methods.go b/internal/suites/action_2fa_methods.go
index 55d90fa6b..96f35e7ca 100644
--- a/internal/suites/action_2fa_methods.go
+++ b/internal/suites/action_2fa_methods.go
@@ -1,17 +1,17 @@
package suites
import (
- "context"
"fmt"
"testing"
+ "github.com/go-rod/rod"
"github.com/stretchr/testify/require"
)
-func (wds *WebDriverSession) doChangeMethod(ctx context.Context, t *testing.T, method string) {
- err := wds.WaitElementLocatedByID(ctx, t, "methods-button").Click()
+func (rs *RodSession) doChangeMethod(t *testing.T, page *rod.Page, method string) {
+ err := rs.WaitElementLocatedByCSSSelector(t, page, "methods-button").Click("left")
require.NoError(t, err)
- wds.WaitElementLocatedByID(ctx, t, "methods-dialog")
- err = wds.WaitElementLocatedByID(ctx, t, fmt.Sprintf("%s-option", method)).Click()
+ rs.WaitElementLocatedByCSSSelector(t, page, "methods-dialog")
+ err = rs.WaitElementLocatedByCSSSelector(t, page, fmt.Sprintf("%s-option", method)).Click("left")
require.NoError(t, err)
}
diff --git a/internal/suites/action_login.go b/internal/suites/action_login.go
index 64ead3a43..fcb281c16 100644
--- a/internal/suites/action_login.go
+++ b/internal/suites/action_login.go
@@ -1,44 +1,44 @@
package suites
import (
- "context"
"testing"
"time"
+ "github.com/go-rod/rod"
"github.com/stretchr/testify/require"
)
-func (wds *WebDriverSession) doFillLoginPageAndClick(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool) {
- usernameElement := wds.WaitElementLocatedByID(ctx, t, "username-textfield")
- err := usernameElement.SendKeys(username)
+func (rs *RodSession) doFillLoginPageAndClick(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool) {
+ usernameElement := rs.WaitElementLocatedByCSSSelector(t, page, "username-textfield")
+ err := usernameElement.Input(username)
require.NoError(t, err)
- passwordElement := wds.WaitElementLocatedByID(ctx, t, "password-textfield")
- err = passwordElement.SendKeys(password)
+ passwordElement := rs.WaitElementLocatedByCSSSelector(t, page, "password-textfield")
+ err = passwordElement.Input(password)
require.NoError(t, err)
if keepMeLoggedIn {
- keepMeLoggedInElement := wds.WaitElementLocatedByID(ctx, t, "remember-checkbox")
- err = keepMeLoggedInElement.Click()
+ keepMeLoggedInElement := rs.WaitElementLocatedByCSSSelector(t, page, "remember-checkbox")
+ err = keepMeLoggedInElement.Click("left")
require.NoError(t, err)
}
- buttonElement := wds.WaitElementLocatedByID(ctx, t, "sign-in-button")
- err = buttonElement.Click()
+ buttonElement := rs.WaitElementLocatedByCSSSelector(t, page, "sign-in-button")
+ err = buttonElement.Click("left")
require.NoError(t, err)
}
// Login 1FA.
-func (wds *WebDriverSession) doLoginOneFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, targetURL string) {
- wds.doVisitLoginPage(ctx, t, targetURL)
- wds.doFillLoginPageAndClick(ctx, t, username, password, keepMeLoggedIn)
+func (rs *RodSession) doLoginOneFactor(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, targetURL string) {
+ rs.doVisitLoginPage(t, page, targetURL)
+ rs.doFillLoginPageAndClick(t, page, username, password, keepMeLoggedIn)
}
// Login 1FA and 2FA subsequently (must already be registered).
-func (wds *WebDriverSession) doLoginTwoFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) {
- wds.doLoginOneFactor(ctx, t, username, password, keepMeLoggedIn, targetURL)
- wds.verifyIsSecondFactorPage(ctx, t)
- wds.doValidateTOTP(ctx, t, otpSecret)
+func (rs *RodSession) doLoginTwoFactor(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) {
+ rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, targetURL)
+ rs.verifyIsSecondFactorPage(t, page)
+ rs.doValidateTOTP(t, page, otpSecret)
// timeout when targetURL is not defined to prevent a show stopping redirect when visiting a protected domain
if targetURL == "" {
time.Sleep(1 * time.Second)
@@ -46,20 +46,20 @@ func (wds *WebDriverSession) doLoginTwoFactor(ctx context.Context, t *testing.T,
}
// Login 1FA and register 2FA.
-func (wds *WebDriverSession) doLoginAndRegisterTOTP(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool) string {
- wds.doLoginOneFactor(ctx, t, username, password, keepMeLoggedIn, "")
- secret := wds.doRegisterTOTP(ctx, t)
- wds.doVisit(t, GetLoginBaseURL())
- wds.verifyIsSecondFactorPage(ctx, t)
+func (rs *RodSession) doLoginAndRegisterTOTP(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool) string {
+ rs.doLoginOneFactor(t, page, username, password, keepMeLoggedIn, "")
+ secret := rs.doRegisterTOTP(t, page)
+ rs.doVisit(t, page, GetLoginBaseURL())
+ rs.verifyIsSecondFactorPage(t, page)
return secret
}
// Register a user with TOTP, logout and then authenticate until TOTP-2FA.
-func (wds *WebDriverSession) doRegisterAndLogin2FA(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, targetURL string) string { //nolint:unparam
+func (rs *RodSession) doRegisterAndLogin2FA(t *testing.T, page *rod.Page, username, password string, keepMeLoggedIn bool, targetURL string) string { //nolint:unparam
// Register TOTP secret and logout.
- secret := wds.doRegisterThenLogout(ctx, t, username, password)
- wds.doLoginTwoFactor(ctx, t, username, password, keepMeLoggedIn, secret, targetURL)
+ secret := rs.doRegisterThenLogout(t, page, username, password)
+ rs.doLoginTwoFactor(t, page, username, password, keepMeLoggedIn, secret, targetURL)
return secret
}
diff --git a/internal/suites/action_logout.go b/internal/suites/action_logout.go
index 36cac95c8..c8c27afcc 100644
--- a/internal/suites/action_logout.go
+++ b/internal/suites/action_logout.go
@@ -1,25 +1,28 @@
package suites
import (
- "context"
"fmt"
"net/url"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) doLogout(ctx context.Context, t *testing.T) {
- wds.doVisit(t, fmt.Sprintf("%s%s", GetLoginBaseURL(), "/logout"))
- wds.verifyIsFirstFactorPage(ctx, t)
+func (rs *RodSession) doLogout(t *testing.T, page *rod.Page) {
+ rs.doVisit(t, page, fmt.Sprintf("%s%s", GetLoginBaseURL(), "/logout"))
+ rs.verifyIsFirstFactorPage(t, page)
}
-func (wds *WebDriverSession) doLogoutWithRedirect(ctx context.Context, t *testing.T, targetURL string, firstFactor bool) {
- wds.doVisit(t, fmt.Sprintf("%s%s%s", GetLoginBaseURL(), "/logout?rd=", url.QueryEscape(targetURL)))
+func (rs *RodSession) doLogoutWithRedirect(t *testing.T, page *rod.Page, targetURL string, firstFactor bool) {
+ rs.doVisit(t, page, fmt.Sprintf("%s%s%s", GetLoginBaseURL(), "/logout?rd=", url.QueryEscape(targetURL)))
if firstFactor {
- wds.verifyIsFirstFactorPage(ctx, t)
+ rs.verifyIsFirstFactorPage(t, page)
return
}
- wds.verifyURLIs(ctx, t, targetURL)
+ page.MustElementR("h1", "Public resource")
+
+ rs.verifyURLIs(t, page, targetURL)
}
diff --git a/internal/suites/action_register.go b/internal/suites/action_register.go
index 728e721d1..c8328b5ff 100644
--- a/internal/suites/action_register.go
+++ b/internal/suites/action_register.go
@@ -1,13 +1,14 @@
package suites
import (
- "context"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) doRegisterThenLogout(ctx context.Context, t *testing.T, username, password string) string {
- secret := wds.doLoginAndRegisterTOTP(ctx, t, username, password, false)
- wds.doLogout(ctx, t)
+func (rs *RodSession) doRegisterThenLogout(t *testing.T, page *rod.Page, username, password string) string {
+ secret := rs.doLoginAndRegisterTOTP(t, page, username, password, false)
+ rs.doLogout(t, page)
return secret
}
diff --git a/internal/suites/action_reset_password.go b/internal/suites/action_reset_password.go
index 69f566f9b..d0fa38227 100644
--- a/internal/suites/action_reset_password.go
+++ b/internal/suites/action_reset_password.go
@@ -1,52 +1,60 @@
package suites
import (
- "context"
"testing"
+ "time"
+ "github.com/go-rod/rod"
"github.com/stretchr/testify/require"
)
-func (wds *WebDriverSession) doInitiatePasswordReset(ctx context.Context, t *testing.T, username string) {
- err := wds.WaitElementLocatedByID(ctx, t, "reset-password-button").Click()
+func (rs *RodSession) doInitiatePasswordReset(t *testing.T, page *rod.Page, username string) {
+ err := rs.WaitElementLocatedByCSSSelector(t, page, "reset-password-button").Click("left")
require.NoError(t, err)
// Fill in username
- err = wds.WaitElementLocatedByID(ctx, t, "username-textfield").SendKeys(username)
+ err = rs.WaitElementLocatedByCSSSelector(t, page, "username-textfield").Input(username)
require.NoError(t, err)
// And click on the reset button
- err = wds.WaitElementLocatedByID(ctx, t, "reset-button").Click()
+ err = rs.WaitElementLocatedByCSSSelector(t, page, "reset-button").Click("left")
require.NoError(t, err)
}
-func (wds *WebDriverSession) doCompletePasswordReset(ctx context.Context, t *testing.T, newPassword1, newPassword2 string) {
+func (rs *RodSession) doCompletePasswordReset(t *testing.T, page *rod.Page, newPassword1, newPassword2 string) {
link := doGetLinkFromLastMail(t)
- wds.doVisit(t, link)
+ rs.doVisit(t, page, link)
- err := wds.WaitElementLocatedByID(ctx, t, "password1-textfield").SendKeys(newPassword1)
+ time.Sleep(1 * time.Second)
+
+ err := rs.WaitElementLocatedByCSSSelector(t, page, "password1-textfield").Input(newPassword1)
require.NoError(t, err)
- err = wds.WaitElementLocatedByID(ctx, t, "password2-textfield").SendKeys(newPassword2)
+
+ time.Sleep(1 * time.Second)
+
+ err = rs.WaitElementLocatedByCSSSelector(t, page, "password2-textfield").Input(newPassword2)
require.NoError(t, err)
- err = wds.WaitElementLocatedByID(ctx, t, "reset-button").Click()
+
+ err = rs.WaitElementLocatedByCSSSelector(t, page, "reset-button").Click("left")
require.NoError(t, err)
}
-func (wds *WebDriverSession) doSuccessfullyCompletePasswordReset(ctx context.Context, t *testing.T, newPassword1, newPassword2 string) {
- wds.doCompletePasswordReset(ctx, t, newPassword1, newPassword2)
- wds.verifyIsFirstFactorPage(ctx, t)
+func (rs *RodSession) doSuccessfullyCompletePasswordReset(t *testing.T, page *rod.Page, newPassword1, newPassword2 string) {
+ rs.doCompletePasswordReset(t, page, newPassword1, newPassword2)
+ rs.verifyIsFirstFactorPage(t, page)
}
-func (wds *WebDriverSession) doUnsuccessfulPasswordReset(ctx context.Context, t *testing.T, newPassword1, newPassword2 string) {
- wds.doCompletePasswordReset(ctx, t, newPassword1, newPassword2)
+func (rs *RodSession) doUnsuccessfulPasswordReset(t *testing.T, page *rod.Page, newPassword1, newPassword2 string) {
+ rs.doCompletePasswordReset(t, page, newPassword1, newPassword2)
+ rs.verifyNotificationDisplayed(t, page, "Your supplied password does not meet the password policy requirements.")
}
-func (wds *WebDriverSession) doResetPassword(ctx context.Context, t *testing.T, username, newPassword1, newPassword2 string, unsuccessful bool) {
- wds.doInitiatePasswordReset(ctx, t, username)
+func (rs *RodSession) doResetPassword(t *testing.T, page *rod.Page, username, newPassword1, newPassword2 string, unsuccessful bool) {
+ rs.doInitiatePasswordReset(t, page, username)
// then wait for the "email sent notification"
- wds.verifyMailNotificationDisplayed(ctx, t)
+ rs.verifyMailNotificationDisplayed(t, page)
if unsuccessful {
- wds.doUnsuccessfulPasswordReset(ctx, t, newPassword1, newPassword2)
+ rs.doUnsuccessfulPasswordReset(t, page, newPassword1, newPassword2)
} else {
- wds.doSuccessfullyCompletePasswordReset(ctx, t, newPassword1, newPassword2)
+ rs.doSuccessfullyCompletePasswordReset(t, page, newPassword1, newPassword2)
}
}
diff --git a/internal/suites/action_totp.go b/internal/suites/action_totp.go
index 2461e6a32..95e1a72e4 100644
--- a/internal/suites/action_totp.go
+++ b/internal/suites/action_totp.go
@@ -1,43 +1,42 @@
package suites
import (
- "context"
"strings"
"testing"
"time"
+ "github.com/go-rod/rod"
"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
-func (wds *WebDriverSession) doRegisterTOTP(ctx context.Context, t *testing.T) string {
- err := wds.WaitElementLocatedByID(ctx, t, "register-link").Click()
+func (rs *RodSession) doRegisterTOTP(t *testing.T, page *rod.Page) string {
+ err := rs.WaitElementLocatedByCSSSelector(t, page, "register-link").Click("left")
require.NoError(t, err)
- wds.verifyMailNotificationDisplayed(ctx, t)
+ rs.verifyMailNotificationDisplayed(t, page)
link := doGetLinkFromLastMail(t)
- wds.doVisit(t, link)
- secretURL, err := wds.WaitElementLocatedByID(ctx, t, "secret-url").GetAttribute("value")
+ rs.doVisit(t, page, link)
+ secretURL, err := page.MustElement("#secret-url").Attribute("value")
assert.NoError(t, err)
- secret := secretURL[strings.LastIndex(secretURL, "=")+1:]
+ secret := (*secretURL)[strings.LastIndex(*secretURL, "=")+1:]
assert.NotEqual(t, "", secret)
assert.NotNil(t, secret)
return secret
}
-func (wds *WebDriverSession) doEnterOTP(ctx context.Context, t *testing.T, code string) {
- inputs := wds.WaitElementsLocatedByCSSSelector(ctx, t, "#otp-input input")
+func (rs *RodSession) doEnterOTP(t *testing.T, page *rod.Page, code string) {
+ inputs := rs.WaitElementsLocatedByCSSSelector(t, page, "otp-input input")
for i := 0; i < 6; i++ {
- err := inputs[i].SendKeys(string(code[i]))
- require.NoError(t, err)
+ _ = inputs[i].Input(string(code[i]))
}
}
-func (wds *WebDriverSession) doValidateTOTP(ctx context.Context, t *testing.T, secret string) {
+func (rs *RodSession) doValidateTOTP(t *testing.T, page *rod.Page, secret string) {
code, err := totp.GenerateCode(secret, time.Now())
assert.NoError(t, err)
- wds.doEnterOTP(ctx, t, code)
+ rs.doEnterOTP(t, page, code)
}
diff --git a/internal/suites/action_visit.go b/internal/suites/action_visit.go
index 62c473205..5fb36b70b 100644
--- a/internal/suites/action_visit.go
+++ b/internal/suites/action_visit.go
@@ -1,28 +1,36 @@
package suites
import (
- "context"
"fmt"
"testing"
+ "github.com/go-rod/rod"
+ "github.com/go-rod/rod/lib/proto"
"github.com/stretchr/testify/assert"
)
-func (wds *WebDriverSession) doVisit(t *testing.T, url string) {
- err := wds.WebDriver.Get(url)
+func (rs *RodSession) doCreateTab(t *testing.T, url string) *rod.Page {
+ p, err := rs.WebDriver.MustIncognito().Page(proto.TargetCreateTarget{URL: url})
+ assert.NoError(t, err)
+
+ return p
+}
+
+func (rs *RodSession) doVisit(t *testing.T, page *rod.Page, url string) {
+ err := page.Navigate(url)
assert.NoError(t, err)
}
-func (wds *WebDriverSession) doVisitAndVerifyOneFactorStep(ctx context.Context, t *testing.T, url string) {
- wds.doVisit(t, url)
- wds.verifyIsFirstFactorPage(ctx, t)
+func (rs *RodSession) doVisitAndVerifyOneFactorStep(t *testing.T, page *rod.Page, url string) {
+ rs.doVisit(t, page, url)
+ rs.verifyIsFirstFactorPage(t, page)
}
-func (wds *WebDriverSession) doVisitLoginPage(ctx context.Context, t *testing.T, targetURL string) {
+func (rs *RodSession) doVisitLoginPage(t *testing.T, page *rod.Page, targetURL string) {
suffix := ""
if targetURL != "" {
suffix = fmt.Sprintf("?rd=%s", targetURL)
}
- wds.doVisitAndVerifyOneFactorStep(ctx, t, fmt.Sprintf("%s/%s", GetLoginBaseURL(), suffix))
+ rs.doVisitAndVerifyOneFactorStep(t, page, fmt.Sprintf("%s/%s", GetLoginBaseURL(), suffix))
}
diff --git a/internal/suites/const.go b/internal/suites/const.go
index d1300159e..2a252cfd2 100644
--- a/internal/suites/const.go
+++ b/internal/suites/const.go
@@ -51,7 +51,6 @@ var DuoBaseURL = "https://duo.example.com"
var AutheliaBaseURL = "https://authelia.example.com:9091"
const stringTrue = "true"
-const defaultChromeDriverPort = "4444"
const testUsername = "john"
const testPassword = "password"
diff --git a/internal/suites/example/compose/authelia/resources/reflex.conf b/internal/suites/example/compose/authelia/resources/reflex.conf
index 7be5ae361..68cd2652f 100644
--- a/internal/suites/example/compose/authelia/resources/reflex.conf
+++ b/internal/suites/example/compose/authelia/resources/reflex.conf
@@ -1 +1 @@
--R '^web/' -r '(\.go$|go\.mod|\.sh|\.yml)' -s /resources/run-backend-dev.sh \ No newline at end of file
+-R '^web/' -R 'users.yml' -r '(\.go$|go\.mod|\.sh|\.yml)' -s /resources/run-backend-dev.sh \ No newline at end of file
diff --git a/internal/suites/example/compose/haproxy/haproxy.cfg b/internal/suites/example/compose/haproxy/haproxy.cfg
index bf682ba0f..ed06a4515 100644
--- a/internal/suites/example/compose/haproxy/haproxy.cfg
+++ b/internal/suites/example/compose/haproxy/haproxy.cfg
@@ -4,11 +4,15 @@ global
log stdout format raw local0 debug
defaults
+ default-server init-addr none
mode http
log global
option httplog
option forwardfor
+resolvers docker
+ nameserver ip 127.0.0.11:53
+
frontend fe_api
bind *:8081 ssl crt /usr/local/etc/haproxy/haproxy.pem
@@ -63,13 +67,13 @@ backend be_auth_request
listen be_auth_request_proxy
mode http
bind 127.0.0.1:8085
- server authelia-backend authelia-backend:9091 ssl verify none
+ server authelia-backend authelia-backend:9091 resolvers docker ssl verify none
backend be_authelia
- server authelia-backend authelia-backend:9091 ssl verify none
+ server authelia-backend authelia-backend:9091 resolvers docker ssl verify none
backend fe_authelia
- server authelia-frontend authelia-frontend:3000
+ server authelia-frontend authelia-frontend:3000 resolvers docker
backend be_httpbin
acl remote_user_exist var(req.auth_response_header.remote_user) -m found
@@ -81,10 +85,10 @@ backend be_httpbin
http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
- server httpbin-backend httpbin:8000
+ server httpbin-backend httpbin:8000 resolvers docker
backend be_mail
- server smtp-backend smtp:1080
+ server smtp-backend smtp:1080 resolvers docker
backend be_protected
- server nginx-backend nginx-backend:80
+ server nginx-backend nginx-backend:80 resolvers docker
diff --git a/internal/suites/scenario_available_methods_test.go b/internal/suites/scenario_available_methods_test.go
index 995aa220b..976fb70d0 100644
--- a/internal/suites/scenario_available_methods_test.go
+++ b/internal/suites/scenario_available_methods_test.go
@@ -5,36 +5,34 @@ import (
"log"
"time"
- "github.com/tebeka/selenium"
-
"github.com/authelia/authelia/v4/internal/utils"
)
type AvailableMethodsScenario struct {
- *SeleniumSuite
+ *RodSuite
methods []string
}
func NewAvailableMethodsScenario(methods []string) *AvailableMethodsScenario {
return &AvailableMethodsScenario{
- SeleniumSuite: new(SeleniumSuite),
- methods: methods,
+ RodSuite: new(RodSuite),
+ methods: methods,
}
}
func (s *AvailableMethodsScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.SeleniumSuite.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *AvailableMethodsScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -42,26 +40,30 @@ func (s *AvailableMethodsScenario) TearDownSuite() {
}
func (s *AvailableMethodsScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *AvailableMethodsScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *AvailableMethodsScenario) TestShouldCheckAvailableMethods() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
- methodsButton := s.WaitElementLocatedByID(ctx, s.T(), "methods-button")
- err := methodsButton.Click()
+ methodsButton := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "methods-button")
+ err := methodsButton.Click("left")
s.Assert().NoError(err)
- methodsDialog := s.WaitElementLocatedByID(ctx, s.T(), "methods-dialog")
- options, err := methodsDialog.FindElements(selenium.ByClassName, "method-option")
+ methodsDialog := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "methods-dialog")
+ options, err := methodsDialog.Elements(".method-option")
s.Assert().NoError(err)
s.Assert().Len(options, len(s.methods))
diff --git a/internal/suites/scenario_bypass_policy_test.go b/internal/suites/scenario_bypass_policy_test.go
index 85f3e041d..497b837e6 100644
--- a/internal/suites/scenario_bypass_policy_test.go
+++ b/internal/suites/scenario_bypass_policy_test.go
@@ -11,27 +11,27 @@ import (
)
type BypassPolicyScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewBypassPolicyScenario() *BypassPolicyScenario {
return &BypassPolicyScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *BypassPolicyScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *BypassPolicyScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -39,23 +39,27 @@ func (s *BypassPolicyScenario) TearDownSuite() {
}
func (s *BypassPolicyScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *BypassPolicyScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *BypassPolicyScenario) TestShouldAccessPublicResource() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), AdminBaseURL)
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), AdminBaseURL)
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
- s.doVisit(s.T(), fmt.Sprintf("%s/secret.html", PublicBaseURL))
- s.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", PublicBaseURL))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
func TestBypassPolicyScenario(t *testing.T) {
diff --git a/internal/suites/scenario_custom_headers_test.go b/internal/suites/scenario_custom_headers_test.go
index ea5edcde4..e03c903ec 100644
--- a/internal/suites/scenario_custom_headers_test.go
+++ b/internal/suites/scenario_custom_headers_test.go
@@ -10,33 +10,31 @@ import (
"time"
mapset "github.com/deckarep/golang-set"
- "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
- "github.com/tebeka/selenium"
)
type CustomHeadersScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewCustomHeadersScenario() *CustomHeadersScenario {
return &CustomHeadersScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *CustomHeadersScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *CustomHeadersScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -44,23 +42,26 @@ func (s *CustomHeadersScenario) TearDownSuite() {
}
func (s *CustomHeadersScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *CustomHeadersScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *CustomHeadersScenario) TestShouldNotForwardCustomHeaderForUnauthenticatedUser() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), fmt.Sprintf("%s/headers", PublicBaseURL))
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/headers", PublicBaseURL))
- body, err := s.WebDriver().FindElement(selenium.ByTagName, "body")
+ body, err := s.Context(ctx).Element("body")
s.Assert().NoError(err)
- s.WaitElementTextContains(ctx, s.T(), body, "\"Host\"")
b, err := body.Text()
s.Assert().NoError(err)
@@ -82,46 +83,46 @@ type HeadersPayload struct {
}
func (s *CustomHeadersScenario) TestShouldForwardCustomHeaderForAuthenticatedUser() {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
expectedGroups := mapset.NewSetWith("dev", "admins")
targetURL := fmt.Sprintf("%s/headers", PublicBaseURL)
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
- s.verifyURLIs(ctx, s.T(), targetURL)
-
- err := s.Wait(ctx, func(d selenium.WebDriver) (bool, error) {
- body, err := s.WebDriver().FindElement(selenium.ByTagName, "body")
- if err != nil {
- return false, err
- }
-
- if body == nil {
- return false, nil
- }
-
- content, err := body.Text()
- if err != nil {
- return false, err
- }
-
- payload := HeadersPayload{}
- if err := json.Unmarshal([]byte(content), &payload); err != nil {
- return false, err
- }
-
- groups := strings.Split(payload.Headers.ForwardedGroups, ",")
- actualGroups := mapset.NewSet()
- for _, group := range groups {
- actualGroups.Add(group)
- }
-
- return strings.Contains(payload.Headers.ForwardedUser, "john") && expectedGroups.Equal(actualGroups) &&
- strings.Contains(payload.Headers.ForwardedName, "John Doe") && strings.Contains(payload.Headers.ForwardedEmail, "john.doe@authelia.com"), nil
- })
-
- require.NoError(s.T(), err)
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
+ s.verifyIsPublic(s.T(), s.Context(ctx))
+
+ body, err := s.Context(ctx).Element("body")
+ s.Assert().NoError(err)
+ s.Assert().NotNil(body)
+
+ content, err := body.Text()
+ s.Assert().NoError(err)
+ s.Assert().NotNil(content)
+
+ payload := HeadersPayload{}
+ if err := json.Unmarshal([]byte(content), &payload); err != nil {
+ log.Panic(err)
+ }
+
+ groups := strings.Split(payload.Headers.ForwardedGroups, ",")
+ actualGroups := mapset.NewSet()
+
+ for _, group := range groups {
+ actualGroups.Add(group)
+ }
+
+ if strings.Contains(payload.Headers.ForwardedUser, "john") && expectedGroups.Equal(actualGroups) &&
+ strings.Contains(payload.Headers.ForwardedName, "John Doe") && strings.Contains(payload.Headers.ForwardedEmail, "john.doe@authelia.com") {
+ err = nil
+ } else {
+ err = fmt.Errorf("headers do not include user information")
+ }
+
+ s.Require().NoError(err)
}
func TestCustomHeadersScenario(t *testing.T) {
diff --git a/internal/suites/scenario_default_redirection_url_test.go b/internal/suites/scenario_default_redirection_url_test.go
index b4dd7c65b..6dd521309 100644
--- a/internal/suites/scenario_default_redirection_url_test.go
+++ b/internal/suites/scenario_default_redirection_url_test.go
@@ -11,57 +11,62 @@ import (
)
type DefaultRedirectionURLScenario struct {
- *SeleniumSuite
+ *RodSuite
secret string
}
func NewDefaultRedirectionURLScenario() *DefaultRedirectionURLScenario {
return &DefaultRedirectionURLScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
-func (drus *DefaultRedirectionURLScenario) SetupSuite() {
- wds, err := StartWebDriver()
+func (s *DefaultRedirectionURLScenario) SetupSuite() {
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- drus.WebDriverSession = wds
-
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
-
- targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- drus.secret = drus.doRegisterAndLogin2FA(ctx, drus.T(), "john", "password", false, targetURL)
- drus.verifySecretAuthorized(ctx, drus.T())
+ s.RodSession = browser
}
-func (drus *DefaultRedirectionURLScenario) TearDownSuite() {
- err := drus.WebDriverSession.Stop()
+func (s *DefaultRedirectionURLScenario) TearDownSuite() {
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
}
}
-func (drus *DefaultRedirectionURLScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+func (s *DefaultRedirectionURLScenario) SetupTest() {
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- drus.doLogout(ctx, drus.T())
- drus.doVisit(drus.T(), HomeBaseURL)
- drus.verifyIsHome(ctx, drus.T())
+func (s *DefaultRedirectionURLScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
-func (drus *DefaultRedirectionURLScenario) TestUserIsRedirectedToDefaultURL() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+func (s *DefaultRedirectionURLScenario) TestUserIsRedirectedToDefaultURL() {
+ ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
+
+ 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.verifySecretAuthorized(s.T(), s.Context(ctx))
+ s.doLogout(s.T(), s.Context(ctx))
- drus.doLoginTwoFactor(ctx, drus.T(), "john", "password", false, drus.secret, "")
- drus.verifyURLIs(ctx, drus.T(), HomeBaseURL+"/")
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, s.secret, "")
+ s.verifyIsHome(s.T(), s.Page)
}
func TestShouldRunDefaultRedirectionURLScenario(t *testing.T) {
diff --git a/internal/suites/scenario_inactivity_test.go b/internal/suites/scenario_inactivity_test.go
index 83cbdc14e..778b183cd 100644
--- a/internal/suites/scenario_inactivity_test.go
+++ b/internal/suites/scenario_inactivity_test.go
@@ -11,35 +11,42 @@ import (
)
type InactivityScenario struct {
- *SeleniumSuite
+ *RodSuite
secret string
}
func NewInactivityScenario() *InactivityScenario {
return &InactivityScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *InactivityScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ s.collectCoverage(s.Page)
+ s.MustClose()
+ }()
+
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- s.secret = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, targetURL)
- s.verifySecretAuthorized(ctx, s.T())
+ s.secret = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, targetURL)
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
func (s *InactivityScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -47,71 +54,78 @@ func (s *InactivityScenario) TearDownSuite() {
}
func (s *InactivityScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *InactivityScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *InactivityScenario) TestShouldRequireReauthenticationAfterInactivityPeriod() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, s.secret, "")
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, s.secret, "")
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
time.Sleep(6 * time.Second)
- s.doVisit(s.T(), targetURL)
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), targetURL)
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
}
func (s *InactivityScenario) TestShouldRequireReauthenticationAfterCookieExpiration() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, s.secret, "")
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, s.secret, "")
for i := 0; i < 3; i++ {
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
time.Sleep(2 * time.Second)
- s.doVisit(s.T(), targetURL)
- s.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), targetURL)
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
-
time.Sleep(2 * time.Second)
- s.doVisit(s.T(), targetURL)
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), targetURL)
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
}
func (s *InactivityScenario) TestShouldDisableCookieExpirationAndInactivity() {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", true, s.secret, "")
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", true, s.secret, "")
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
time.Sleep(10 * time.Second)
- s.doVisit(s.T(), targetURL)
- s.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), targetURL)
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
func TestInactivityScenario(t *testing.T) {
diff --git a/internal/suites/scenario_oidc_test.go b/internal/suites/scenario_oidc_test.go
index d531a2dab..f97ce0e14 100644
--- a/internal/suites/scenario_oidc_test.go
+++ b/internal/suites/scenario_oidc_test.go
@@ -12,33 +12,40 @@ import (
)
type OIDCScenario struct {
- *SeleniumSuite
+ *RodSuite
secret string
}
func NewOIDCScenario() *OIDCScenario {
return &OIDCScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *OIDCScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ 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.secret = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, AdminBaseURL)
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.secret = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, AdminBaseURL)
}
func (s *OIDCScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -47,65 +54,73 @@ func (s *OIDCScenario) TearDownSuite() {
func (s *OIDCScenario) SetupTest() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
+
+ s.Page = s.doCreateTab(s.T(), fmt.Sprintf("%s/logout", OIDCBaseURL))
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
+}
- s.doVisit(s.T(), fmt.Sprintf("%s/logout", OIDCBaseURL))
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *OIDCScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *OIDCScenario) TestShouldAuthorizeAccessToOIDCApp() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), OIDCBaseURL)
- s.verifyIsFirstFactorPage(ctx, s.T())
- s.doFillLoginPageAndClick(ctx, s.T(), "john", "password", false)
- s.verifyIsSecondFactorPage(ctx, s.T())
- s.doValidateTOTP(ctx, s.T(), s.secret)
- time.Sleep(2 * time.Second)
+ s.doVisit(s.T(), s.Context(ctx), OIDCBaseURL)
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
+ s.doFillLoginPageAndClick(s.T(), s.Context(ctx), "john", "password", false)
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
+ s.doValidateTOTP(s.T(), s.Context(ctx), s.secret)
- s.waitBodyContains(ctx, s.T(), "Not logged yet...")
+ s.waitBodyContains(s.T(), s.Context(ctx), "Not logged yet...")
- // this href represents the 'login' link
- err := s.WaitElementLocatedByTagName(ctx, s.T(), "a").Click()
+ // Search for the 'login' link
+ err := s.Page.MustSearch("Log in").Click("left")
assert.NoError(s.T(), err)
- s.verifyIsConsentPage(ctx, s.T())
-
- err = s.WaitElementLocatedByID(ctx, s.T(), "accept-button").Click()
+ s.verifyIsConsentPage(s.T(), s.Context(ctx))
+ err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "accept-button").Click("left")
assert.NoError(s.T(), err)
// Verify that the app is showing the info related to the user stored in the JWT token
- time.Sleep(2 * time.Second)
- s.waitBodyContains(ctx, s.T(), "Logged in as john!")
+ s.waitBodyContains(s.T(), s.Context(ctx), "Logged in as john!")
}
func (s *OIDCScenario) TestShouldDenyConsent() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), OIDCBaseURL)
- s.verifyIsFirstFactorPage(ctx, s.T())
- s.doFillLoginPageAndClick(ctx, s.T(), "john", "password", false)
- s.verifyIsSecondFactorPage(ctx, s.T())
- s.doValidateTOTP(ctx, s.T(), s.secret)
- time.Sleep(1 * time.Second)
+ s.doVisit(s.T(), s.Context(ctx), OIDCBaseURL)
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
+ s.doFillLoginPageAndClick(s.T(), s.Context(ctx), "john", "password", false)
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
+ s.doValidateTOTP(s.T(), s.Context(ctx), s.secret)
- s.waitBodyContains(ctx, s.T(), "Not logged yet...")
+ s.waitBodyContains(s.T(), s.Context(ctx), "Not logged yet...")
- // this href represents the 'login' link
- err := s.WaitElementLocatedByTagName(ctx, s.T(), "a").Click()
+ // Search for the 'login' link
+ err := s.Page.MustSearch("Log in").Click("left")
assert.NoError(s.T(), err)
- s.verifyIsConsentPage(ctx, s.T())
+ s.verifyIsConsentPage(s.T(), s.Context(ctx))
- err = s.WaitElementLocatedByID(ctx, s.T(), "deny-button").Click()
+ err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "deny-button").Click("left")
assert.NoError(s.T(), err)
- time.Sleep(1 * time.Second)
- s.verifyURLIs(ctx, s.T(), "https://oidc.example.com:8080/oauth2/callback?error=access_denied&error_description=User%20has%20rejected%20the%20scopes")
+ s.verifyIsOIDC(s.T(), s.Context(ctx), "oauth2:", "https://oidc.example.com:8080/oauth2/callback?error=access_denied&error_description=User%20has%20rejected%20the%20scopes")
}
func TestRunOIDCScenario(t *testing.T) {
diff --git a/internal/suites/scenario_one_factor_test.go b/internal/suites/scenario_one_factor_test.go
index febfae7b2..312d66578 100644
--- a/internal/suites/scenario_one_factor_test.go
+++ b/internal/suites/scenario_one_factor_test.go
@@ -11,27 +11,27 @@ import (
)
type OneFactorSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewOneFactorScenario() *OneFactorSuite {
return &OneFactorSuite{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *OneFactorSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *OneFactorSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -39,40 +39,50 @@ func (s *OneFactorSuite) TearDownSuite() {
}
func (s *OneFactorSuite) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *OneFactorSuite) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *OneFactorSuite) TestShouldAuthorizeSecretAfterOneFactor() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html", SingleFactorBaseURL)
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
- s.verifySecretAuthorized(ctx, s.T())
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
+ s.verifySecretAuthorized(s.T(), s.Page)
}
func (s *OneFactorSuite) TestShouldRedirectToSecondFactor() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
}
func (s *OneFactorSuite) TestShouldDenyAccessOnBadPassword() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- s.doLoginOneFactor(ctx, s.T(), "john", "bad-password", false, targetURL)
- s.verifyIsFirstFactorPage(ctx, s.T())
- s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "bad-password", false, targetURL)
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
}
func TestRunOneFactor(t *testing.T) {
diff --git a/internal/suites/scenario_password_complexity_test.go b/internal/suites/scenario_password_complexity_test.go
index 493f6293a..78e44d0c4 100644
--- a/internal/suites/scenario_password_complexity_test.go
+++ b/internal/suites/scenario_password_complexity_test.go
@@ -10,25 +10,25 @@ import (
)
type PasswordComplexityScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewPasswordComplexityScenario() *PasswordComplexityScenario {
- return &PasswordComplexityScenario{SeleniumSuite: new(SeleniumSuite)}
+ return &PasswordComplexityScenario{RodSuite: new(RodSuite)}
}
func (s *PasswordComplexityScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *PasswordComplexityScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -36,24 +36,28 @@ func (s *PasswordComplexityScenario) TearDownSuite() {
}
func (s *PasswordComplexityScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *PasswordComplexityScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *PasswordComplexityScenario) TestShouldRejectPasswordReset() {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
// Attempt to reset the password to a
- s.doResetPassword(ctx, s.T(), "john", "a", "a", true)
- s.verifyNotificationDisplayed(ctx, s.T(), "Your supplied password does not meet the password policy requirements.")
+ s.doResetPassword(s.T(), s.Context(ctx), "john", "a", "a", true)
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Your supplied password does not meet the password policy requirements.")
}
func TestRunPasswordComplexityScenario(t *testing.T) {
diff --git a/internal/suites/scenario_redirection_check_test.go b/internal/suites/scenario_redirection_check_test.go
index af5f8b2e6..4d0b7c4f6 100644
--- a/internal/suites/scenario_redirection_check_test.go
+++ b/internal/suites/scenario_redirection_check_test.go
@@ -10,27 +10,27 @@ import (
)
type RedirectionCheckScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewRedirectionCheckScenario() *RedirectionCheckScenario {
return &RedirectionCheckScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *RedirectionCheckScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *RedirectionCheckScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -38,12 +38,13 @@ func (s *RedirectionCheckScenario) TearDownSuite() {
}
func (s *RedirectionCheckScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *RedirectionCheckScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
var redirectionAuthorizations = map[string]bool{
@@ -59,21 +60,24 @@ var redirectionAuthorizations = map[string]bool{
func (s *RedirectionCheckScenario) TestShouldRedirectOnLoginOnlyWhenDomainIsSafe() {
ctx, cancel := context.WithTimeout(context.Background(), 35*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
+ secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
for url, redirected := range redirectionAuthorizations {
s.T().Run(url, func(t *testing.T) {
- s.doLoginTwoFactor(ctx, t, "john", "password", false, secret, url)
+ s.doLoginTwoFactor(t, s.Context(ctx), "john", "password", false, secret, url)
if redirected {
- s.verifySecretAuthorized(ctx, t)
+ s.verifySecretAuthorized(t, s.Context(ctx))
} else {
- s.verifyIsAuthenticatedPage(ctx, t)
+ s.verifyIsAuthenticatedPage(t, s.Context(ctx))
}
- s.doLogout(ctx, t)
+ s.doLogout(t, s.Context(ctx))
})
}
}
@@ -91,11 +95,14 @@ var logoutRedirectionURLs = map[string]bool{
func (s *RedirectionCheckScenario) TestShouldRedirectOnLogoutOnlyWhenDomainIsSafe() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
for url, success := range logoutRedirectionURLs {
s.T().Run(url, func(t *testing.T) {
- s.doLogoutWithRedirect(ctx, t, url, !success)
+ s.doLogoutWithRedirect(t, s.Context(ctx), url, !success)
})
}
}
diff --git a/internal/suites/scenario_redirection_url_test.go b/internal/suites/scenario_redirection_url_test.go
index 5224faa20..6ec35c7d7 100644
--- a/internal/suites/scenario_redirection_url_test.go
+++ b/internal/suites/scenario_redirection_url_test.go
@@ -11,50 +11,54 @@ import (
)
type RedirectionURLScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewRedirectionURLScenario() *RedirectionURLScenario {
return &RedirectionURLScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
-func (rus *RedirectionURLScenario) SetupSuite() {
- wds, err := StartWebDriver()
+func (s *RedirectionURLScenario) SetupSuite() {
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- rus.WebDriverSession = wds
+ s.RodSession = browser
}
-func (rus *RedirectionURLScenario) TearDownSuite() {
- err := rus.WebDriverSession.Stop()
+func (s *RedirectionURLScenario) TearDownSuite() {
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
}
}
-func (rus *RedirectionURLScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+func (s *RedirectionURLScenario) SetupTest() {
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- rus.doLogout(ctx, rus.T())
- rus.doVisit(rus.T(), HomeBaseURL)
- rus.verifyIsHome(ctx, rus.T())
+func (s *RedirectionURLScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
-func (rus *RedirectionURLScenario) TestShouldVerifyCustomURLParametersArePropagatedAfterRedirection() {
+func (s *RedirectionURLScenario) TestShouldVerifyCustomURLParametersArePropagatedAfterRedirection() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html?myparam=test", SingleFactorBaseURL)
- rus.doLoginOneFactor(ctx, rus.T(), "john", "password", false, targetURL)
- rus.verifySecretAuthorized(ctx, rus.T())
- rus.verifyURLIs(ctx, rus.T(), targetURL)
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, targetURL)
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
+ s.verifyURLIs(s.T(), s.Context(ctx), targetURL)
}
func TestRedirectionURLScenario(t *testing.T) {
diff --git a/internal/suites/scenario_regulation_test.go b/internal/suites/scenario_regulation_test.go
index a350b8593..272e1a526 100644
--- a/internal/suites/scenario_regulation_test.go
+++ b/internal/suites/scenario_regulation_test.go
@@ -11,27 +11,27 @@ import (
)
type RegulationScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewRegulationScenario() *RegulationScenario {
return &RegulationScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *RegulationScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *RegulationScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -39,47 +39,49 @@ func (s *RegulationScenario) TearDownSuite() {
}
func (s *RegulationScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *RegulationScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *RegulationScenario) TestShouldBanUserAfterTooManyAttempt() {
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisitLoginPage(ctx, s.T(), "")
- s.doFillLoginPageAndClick(ctx, s.T(), "john", "bad-password", false)
- s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
+ s.doVisitLoginPage(s.T(), s.Context(ctx), "")
+ s.doFillLoginPageAndClick(s.T(), s.Context(ctx), "john", "bad-password", false)
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
for i := 0; i < 3; i++ {
- err := s.WaitElementLocatedByID(ctx, s.T(), "password-textfield").SendKeys("bad-password")
+ err := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "password-textfield").Input("bad-password")
require.NoError(s.T(), err)
- err = s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
+ err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "sign-in-button").Click("left")
require.NoError(s.T(), err)
- time.Sleep(1 * time.Second)
}
// Enter the correct password and test the regulation lock out
- err := s.WaitElementLocatedByID(ctx, s.T(), "password-textfield").SendKeys("password")
+ err := s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "password-textfield").Input("password")
require.NoError(s.T(), err)
- err = s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
+ err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "sign-in-button").Click("left")
require.NoError(s.T(), err)
- s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
- time.Sleep(1 * time.Second)
- s.verifyIsFirstFactorPage(ctx, s.T())
- time.Sleep(9 * time.Second)
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
+ time.Sleep(10 * time.Second)
// Enter the correct password and test a successful login
- err = s.WaitElementLocatedByID(ctx, s.T(), "password-textfield").SendKeys("password")
+ err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "password-textfield").Input("password")
require.NoError(s.T(), err)
- err = s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
+ err = s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "sign-in-button").Click("left")
require.NoError(s.T(), err)
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
}
func TestBlacklistingScenario(t *testing.T) {
diff --git a/internal/suites/scenario_reset_password_test.go b/internal/suites/scenario_reset_password_test.go
index 203546611..755deb7a6 100644
--- a/internal/suites/scenario_reset_password_test.go
+++ b/internal/suites/scenario_reset_password_test.go
@@ -10,25 +10,25 @@ import (
)
type ResetPasswordScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewResetPasswordScenario() *ResetPasswordScenario {
- return &ResetPasswordScenario{SeleniumSuite: new(SeleniumSuite)}
+ return &ResetPasswordScenario{RodSuite: new(RodSuite)}
}
func (s *ResetPasswordScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *ResetPasswordScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -36,64 +36,74 @@ func (s *ResetPasswordScenario) TearDownSuite() {
}
func (s *ResetPasswordScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *ResetPasswordScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *ResetPasswordScenario) TestShouldResetPassword() {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
// Reset the password to abc
- s.doResetPassword(ctx, s.T(), "john", "abc", "abc", false)
+ s.doResetPassword(s.T(), s.Context(ctx), "john", "abc", "abc", false)
// Try to login with the old password
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyNotificationDisplayed(ctx, s.T(), "Incorrect username or password.")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Incorrect username or password.")
// Try to login with the new password
- s.doLoginOneFactor(ctx, s.T(), "john", "abc", false, "")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "abc", false, "")
// Logout
- s.doLogout(ctx, s.T())
+ s.doLogout(s.T(), s.Context(ctx))
// Reset the original password
- s.doResetPassword(ctx, s.T(), "john", "password", "password", false)
+ s.doResetPassword(s.T(), s.Context(ctx), "john", "password", "password", false)
}
func (s *ResetPasswordScenario) TestShouldMakeAttackerThinkPasswordResetIsInitiated() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
// Try to initiate a password reset of an nonexistent user.
- s.doInitiatePasswordReset(ctx, s.T(), "i_dont_exist")
+ s.doInitiatePasswordReset(s.T(), s.Context(ctx), "i_dont_exist")
// Check that the notification make the attacker thinks the process is initiated
- s.verifyMailNotificationDisplayed(ctx, s.T())
+ s.verifyMailNotificationDisplayed(s.T(), s.Context(ctx))
}
func (s *ResetPasswordScenario) TestShouldLetUserNoticeThereIsAPasswordMismatch() {
- ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
- s.doInitiatePasswordReset(ctx, s.T(), "john")
- s.verifyMailNotificationDisplayed(ctx, s.T())
+ s.doInitiatePasswordReset(s.T(), s.Context(ctx), "john")
+ s.verifyMailNotificationDisplayed(s.T(), s.Context(ctx))
- s.doCompletePasswordReset(ctx, s.T(), "password", "another_password")
- s.verifyNotificationDisplayed(ctx, s.T(), "Passwords do not match.")
+ s.doCompletePasswordReset(s.T(), s.Context(ctx), "password", "another_password")
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Passwords do not match.")
}
func TestRunResetPasswordScenario(t *testing.T) {
diff --git a/internal/suites/scenario_signin_email_test.go b/internal/suites/scenario_signin_email_test.go
index 9b1ea897f..09e9eb647 100644
--- a/internal/suites/scenario_signin_email_test.go
+++ b/internal/suites/scenario_signin_email_test.go
@@ -13,27 +13,27 @@ import (
)
type SigninEmailScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewSigninEmailScenario() *SigninEmailScenario {
return &SigninEmailScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *SigninEmailScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *SigninEmailScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -41,21 +41,25 @@ func (s *SigninEmailScenario) TearDownSuite() {
}
func (s *SigninEmailScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *SigninEmailScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *SigninEmailScenario) TestShouldSignInWithUserEmail() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
targetURL := fmt.Sprintf("%s/secret.html", SingleFactorBaseURL)
- s.doLoginOneFactor(ctx, s.T(), "john.doe@authelia.com", "password", false, targetURL)
- s.verifySecretAuthorized(ctx, s.T())
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john.doe@authelia.com", "password", false, targetURL)
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
func TestSigninEmailScenario(t *testing.T) {
diff --git a/internal/suites/scenario_two_factor_test.go b/internal/suites/scenario_two_factor_test.go
index 9ff5c6e24..79288467b 100644
--- a/internal/suites/scenario_two_factor_test.go
+++ b/internal/suites/scenario_two_factor_test.go
@@ -11,27 +11,27 @@ import (
)
type TwoFactorSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewTwoFactorScenario() *TwoFactorSuite {
return &TwoFactorSuite{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *TwoFactorSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *TwoFactorSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -39,62 +39,57 @@ func (s *TwoFactorSuite) TearDownSuite() {
}
func (s *TwoFactorSuite) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *TwoFactorSuite) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *TwoFactorSuite) TestShouldAuthorizeSecretAfterTwoFactor() {
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
username := testUsername
password := testPassword
- // Login one factor
- s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
-
- // Check he reaches the 2FA stage
- s.verifyIsSecondFactorPage(ctx, s.T())
-
- // Then register the TOTP factor
- secret := s.doRegisterTOTP(ctx, s.T())
-
- // And logout
- s.doLogout(ctx, s.T())
-
- // Login again with 1FA & 2FA
+ // Login and register TOTP, logout and login again with 1FA & 2FA
targetURL := fmt.Sprintf("%s/secret.html", AdminBaseURL)
- s.doLoginTwoFactor(ctx, s.T(), testUsername, testPassword, false, secret, targetURL)
+ _ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), username, password, false, targetURL)
// And check if the user is redirected to the secret.
- s.verifySecretAuthorized(ctx, s.T())
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
// Leave the secret
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// And try to reload it again to check the session is kept
- s.doVisit(s.T(), targetURL)
- s.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), targetURL)
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
func (s *TwoFactorSuite) TestShouldFailTwoFactor() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
// Register TOTP secret and logout.
- s.doRegisterThenLogout(ctx, s.T(), testUsername, testPassword)
+ s.doRegisterThenLogout(s.T(), s.Context(ctx), testUsername, testPassword)
wrongPasscode := "123456"
- s.doLoginOneFactor(ctx, s.T(), testUsername, testPassword, false, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
- s.doEnterOTP(ctx, s.T(), wrongPasscode)
- s.verifyNotificationDisplayed(ctx, s.T(), "The one-time password might be wrong")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), testUsername, testPassword, false, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
+ s.doEnterOTP(s.T(), s.Context(ctx), wrongPasscode)
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "The one-time password might be wrong")
}
func TestRunTwoFactor(t *testing.T) {
diff --git a/internal/suites/scenario_user_preferences_test.go b/internal/suites/scenario_user_preferences_test.go
index b9620dd8f..4d51509f2 100644
--- a/internal/suites/scenario_user_preferences_test.go
+++ b/internal/suites/scenario_user_preferences_test.go
@@ -10,27 +10,27 @@ import (
)
type UserPreferencesScenario struct {
- *SeleniumSuite
+ *RodSuite
}
func NewUserPreferencesScenario() *UserPreferencesScenario {
return &UserPreferencesScenario{
- SeleniumSuite: new(SeleniumSuite),
+ RodSuite: new(RodSuite),
}
}
func (s *UserPreferencesScenario) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *UserPreferencesScenario) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -38,59 +38,63 @@ func (s *UserPreferencesScenario) TearDownSuite() {
}
func (s *UserPreferencesScenario) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *UserPreferencesScenario) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *UserPreferencesScenario) TestShouldRememberLastUsed2FAMethod() {
- ctx, cancel := context.WithTimeout(context.Background(), 40*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
// Authenticate
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
// Then switch to push notification method
- s.doChangeMethod(ctx, s.T(), "push-notification")
- s.WaitElementLocatedByID(ctx, s.T(), "push-notification-method")
+ s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
+ s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "push-notification-method")
// Switch context to clean up state in portal.
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Then go back to portal.
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
// And check the latest method is still used.
- s.WaitElementLocatedByID(ctx, s.T(), "push-notification-method")
+ s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "push-notification-method")
// Meaning the authentication is successful
- s.verifyIsHome(ctx, s.T())
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Logout the user and see what user 'harry' sees.
- s.doLogout(ctx, s.T())
- s.doLoginOneFactor(ctx, s.T(), "harry", "password", false, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
- s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
+ s.doLogout(s.T(), s.Context(ctx))
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "harry", "password", false, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
+ s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "one-time-password-method")
- s.doLogout(ctx, s.T())
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doLogout(s.T(), s.Context(ctx))
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
// Then log back as previous user and verify the push notification is still the default method
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
- s.WaitElementLocatedByID(ctx, s.T(), "push-notification-method")
- s.verifyIsHome(ctx, s.T())
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
+ s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "push-notification-method")
+ s.verifyIsHome(s.T(), s.Context(ctx))
- s.doLogout(ctx, s.T())
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
+ s.doLogout(s.T(), s.Context(ctx))
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
// Eventually restore the default method
- s.doChangeMethod(ctx, s.T(), "one-time-password")
- s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
+ s.doChangeMethod(s.T(), s.Context(ctx), "one-time-password")
+ s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "one-time-password-method")
}
func TestUserPreferencesScenario(t *testing.T) {
diff --git a/internal/suites/suite_activedirectory_test.go b/internal/suites/suite_activedirectory_test.go
index c99bf479b..48e7f9b0a 100644
--- a/internal/suites/suite_activedirectory_test.go
+++ b/internal/suites/suite_activedirectory_test.go
@@ -7,11 +7,11 @@ import (
)
type ActiveDirectorySuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewActiveDirectorySuite() *ActiveDirectorySuite {
- return &ActiveDirectorySuite{SeleniumSuite: new(SeleniumSuite)}
+ return &ActiveDirectorySuite{RodSuite: new(RodSuite)}
}
func (s *ActiveDirectorySuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_bypass_all_test.go b/internal/suites/suite_bypass_all_test.go
index c6ae052b5..4450d6206 100644
--- a/internal/suites/suite_bypass_all_test.go
+++ b/internal/suites/suite_bypass_all_test.go
@@ -11,40 +11,53 @@ import (
)
type BypassAllWebDriverSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewBypassAllWebDriverSuite() *BypassAllWebDriverSuite {
- return &BypassAllWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &BypassAllWebDriverSuite{RodSuite: new(RodSuite)}
}
func (s *BypassAllWebDriverSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *BypassAllWebDriverSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
}
}
+func (s *BypassAllWebDriverSuite) SetupTest() {
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
+
+func (s *BypassAllWebDriverSuite) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
+}
+
func (s *BypassAllWebDriverSuite) TestShouldAccessPublicResource() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doVisit(s.T(), fmt.Sprintf("%s/secret.html", AdminBaseURL))
- s.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", AdminBaseURL))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
- s.doVisit(s.T(), fmt.Sprintf("%s/secret.html", PublicBaseURL))
- s.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", PublicBaseURL))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
type BypassAllSuite struct {
diff --git a/internal/suites/suite_docker_test.go b/internal/suites/suite_docker_test.go
index 730735f9b..8b6f23b7d 100644
--- a/internal/suites/suite_docker_test.go
+++ b/internal/suites/suite_docker_test.go
@@ -7,11 +7,11 @@ import (
)
type DockerSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewDockerSuite() *DockerSuite {
- return &DockerSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &DockerSuite{RodSuite: new(RodSuite)}
}
func (s *DockerSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_duo_push_test.go b/internal/suites/suite_duo_push_test.go
index 3a7eaf255..05dcbf788 100644
--- a/internal/suites/suite_duo_push_test.go
+++ b/internal/suites/suite_duo_push_test.go
@@ -10,25 +10,25 @@ import (
)
type DuoPushWebDriverSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewDuoPushWebDriverSuite() *DuoPushWebDriverSuite {
- return &DuoPushWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &DuoPushWebDriverSuite{RodSuite: new(RodSuite)}
}
func (s *DuoPushWebDriverSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *DuoPushWebDriverSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -36,65 +36,75 @@ func (s *DuoPushWebDriverSuite) TearDownSuite() {
}
func (s *DuoPushWebDriverSuite) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- s.doLogout(ctx, s.T())
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
}
func (s *DuoPushWebDriverSuite) TearDownTest() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+
+ s.collectCoverage(s.Page)
+ s.MustClose()
+ }()
- s.doLogout(ctx, s.T())
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
- s.doChangeMethod(ctx, s.T(), "one-time-password")
- s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
+ s.doLogout(s.T(), s.Context(ctx))
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
+ s.doChangeMethod(s.T(), s.Context(ctx), "one-time-password")
+ s.WaitElementLocatedByCSSSelector(s.T(), s.Context(ctx), "one-time-password-method")
}
func (s *DuoPushWebDriverSuite) TestShouldSucceedAuthentication() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
ConfigureDuo(s.T(), Allow)
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.doChangeMethod(ctx, s.T(), "push-notification")
- s.verifyIsHome(ctx, s.T())
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
+ s.verifyIsHome(s.T(), s.Context(ctx))
}
func (s *DuoPushWebDriverSuite) TestShouldFailAuthentication() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
ConfigureDuo(s.T(), Deny)
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.doChangeMethod(ctx, s.T(), "push-notification")
- s.WaitElementLocatedByClassName(ctx, s.T(), "failure-icon")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
+ s.WaitElementLocatedByClassName(s.T(), s.Context(ctx), "failure-icon")
}
type DuoPushDefaultRedirectionSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewDuoPushDefaultRedirectionSuite() *DuoPushDefaultRedirectionSuite {
- return &DuoPushDefaultRedirectionSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &DuoPushDefaultRedirectionSuite{RodSuite: new(RodSuite)}
}
func (s *DuoPushDefaultRedirectionSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *DuoPushDefaultRedirectionSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -102,19 +112,25 @@ func (s *DuoPushDefaultRedirectionSuite) TearDownSuite() {
}
func (s *DuoPushDefaultRedirectionSuite) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
+func (s *DuoPushDefaultRedirectionSuite) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *DuoPushDefaultRedirectionSuite) TestUserIsRedirectedToDefaultURL() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
- defer cancel()
-
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.doChangeMethod(ctx, s.T(), "push-notification")
- s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
+
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.doChangeMethod(s.T(), s.Context(ctx), "push-notification")
+ s.verifyIsHome(s.T(), s.Page)
}
type DuoPushSuite struct {
diff --git a/internal/suites/suite_haproxy.go b/internal/suites/suite_haproxy.go
index a571a47f8..160d1b9ef 100644
--- a/internal/suites/suite_haproxy.go
+++ b/internal/suites/suite_haproxy.go
@@ -42,6 +42,13 @@ func init() {
fmt.Println(frontendLogs)
+ haproxyLogs, err := dockerEnvironment.Logs("haproxy", nil)
+ if err != nil {
+ return err
+ }
+
+ fmt.Println(haproxyLogs)
+
return nil
}
diff --git a/internal/suites/suite_haproxy_test.go b/internal/suites/suite_haproxy_test.go
index 276214a3e..1c66242b2 100644
--- a/internal/suites/suite_haproxy_test.go
+++ b/internal/suites/suite_haproxy_test.go
@@ -7,11 +7,11 @@ import (
)
type HAProxySuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewHAProxySuite() *HAProxySuite {
- return &HAProxySuite{SeleniumSuite: new(SeleniumSuite)}
+ return &HAProxySuite{RodSuite: new(RodSuite)}
}
func (s *HAProxySuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_high_availability_test.go b/internal/suites/suite_high_availability_test.go
index ce5f69136..b18bcfa68 100644
--- a/internal/suites/suite_high_availability_test.go
+++ b/internal/suites/suite_high_availability_test.go
@@ -13,25 +13,25 @@ import (
)
type HighAvailabilityWebDriverSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewHighAvailabilityWebDriverSuite() *HighAvailabilityWebDriverSuite {
- return &HighAvailabilityWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &HighAvailabilityWebDriverSuite{RodSuite: new(RodSuite)}
}
func (s *HighAvailabilityWebDriverSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *HighAvailabilityWebDriverSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -39,37 +39,42 @@ func (s *HighAvailabilityWebDriverSuite) TearDownSuite() {
}
func (s *HighAvailabilityWebDriverSuite) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *HighAvailabilityWebDriverSuite) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActive() {
- ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
+ secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
err := haDockerEnvironment.Restart("redis-node-0")
s.Require().NoError(err)
- time.Sleep(5 * time.Second)
-
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
}
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrimaryRedisNodeFailure() {
- ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
+ secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
err := haDockerEnvironment.Stop("redis-node-0")
s.Require().NoError(err)
@@ -79,32 +84,32 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrim
s.Require().NoError(err)
}()
- // Allow fail over to occur.
- time.Sleep(3 * time.Second)
-
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Verify the user is still authenticated
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
// Then logout and login again to check we can see the secret.
- s.doLogout(ctx, s.T())
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doLogout(s.T(), s.Context(ctx))
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
- s.verifySecretAuthorized(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrimaryRedisSentinelFailureAndSecondaryRedisNodeFailure() {
- ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
+ secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
err := haDockerEnvironment.Stop("redis-sentinel-0")
s.Require().NoError(err)
@@ -122,38 +127,39 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserSessionActiveWithPrim
s.Require().NoError(err)
}()
- // Allow fail over to occur.
- time.Sleep(3 * time.Second)
-
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Verify the user is still authenticated
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
}
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserDataInDB() {
- ctx, cancel := context.WithTimeout(context.Background(), 50*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- secret := s.doRegisterThenLogout(ctx, s.T(), "john", "password")
+ secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
err := haDockerEnvironment.Restart("mariadb")
s.Require().NoError(err)
- time.Sleep(20 * time.Second)
-
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
}
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepSessionAfterAutheliaRestart() {
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- secret := s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
- s.verifyIsSecondFactorPage(ctx, s.T())
+ secret := s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
err := haDockerEnvironment.Restart("authelia-backend")
s.Require().NoError(err)
@@ -161,19 +167,19 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldKeepSessionAfterAutheliaResta
err = waitUntilAutheliaBackendIsReady(haDockerEnvironment)
s.Require().NoError(err)
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Verify the user is still authenticated
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsSecondFactorPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsSecondFactorPage(s.T(), s.Context(ctx))
// Then logout and login again to check the secret is still there
- s.doLogout(ctx, s.T())
- s.verifyIsFirstFactorPage(ctx, s.T())
+ s.doLogout(s.T(), s.Context(ctx))
+ s.verifyIsFirstFactorPage(s.T(), s.Context(ctx))
- s.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
- s.verifySecretAuthorized(ctx, s.T())
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, fmt.Sprintf("%s/secret.html", SecureBaseURL))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
var UserJohn = "john"
@@ -220,31 +226,34 @@ var expectedAuthorizations = map[string](map[string]bool){
}
func (s *HighAvailabilityWebDriverSuite) TestShouldVerifyAccessControl() {
- verifyUserIsAuthorized := func(ctx context.Context, t *testing.T, username, targetURL string, authorized bool) { //nolint:unparam
- s.doVisit(t, targetURL)
- s.verifyURLIs(ctx, t, targetURL)
+ verifyUserIsAuthorized := func(ctx context.Context, t *testing.T, targetURL string, authorized bool) {
+ s.doVisit(t, s.Context(ctx), targetURL)
+ s.verifyURLIs(t, s.Context(ctx), targetURL)
if authorized {
- s.verifySecretAuthorized(ctx, t)
+ s.verifySecretAuthorized(t, s.Context(ctx))
} else {
- s.verifyBodyContains(ctx, t, "403 Forbidden")
+ s.verifyBodyContains(t, s.Context(ctx), "403 Forbidden")
}
}
verifyAuthorization := func(username string) func(t *testing.T) {
return func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ s.collectScreenshot(ctx.Err(), s.Page)
+ cancel()
+ }()
- s.doRegisterAndLogin2FA(ctx, t, username, "password", false, "")
+ s.doRegisterAndLogin2FA(t, s.Context(ctx), username, "password", false, "")
for url, authorizations := range expectedAuthorizations {
t.Run(url, func(t *testing.T) {
- verifyUserIsAuthorized(ctx, t, username, url, authorizations[username])
+ verifyUserIsAuthorized(ctx, t, url, authorizations[username])
})
}
- s.doLogout(ctx, t)
+ s.doLogout(t, s.Context(ctx))
}
}
diff --git a/internal/suites/suite_kubernetes_test.go b/internal/suites/suite_kubernetes_test.go
index 8cb61c71b..a40889dd7 100644
--- a/internal/suites/suite_kubernetes_test.go
+++ b/internal/suites/suite_kubernetes_test.go
@@ -7,11 +7,11 @@ import (
)
type KubernetesSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewKubernetesSuite() *KubernetesSuite {
- return &KubernetesSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &KubernetesSuite{RodSuite: new(RodSuite)}
}
func (s *KubernetesSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_ldap_test.go b/internal/suites/suite_ldap_test.go
index 5859d936f..96334f8a3 100644
--- a/internal/suites/suite_ldap_test.go
+++ b/internal/suites/suite_ldap_test.go
@@ -7,11 +7,11 @@ import (
)
type LDAPSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewLDAPSuite() *LDAPSuite {
- return &LDAPSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &LDAPSuite{RodSuite: new(RodSuite)}
}
func (s *LDAPSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_mariadb_test.go b/internal/suites/suite_mariadb_test.go
index cae16a501..eb201aa6b 100644
--- a/internal/suites/suite_mariadb_test.go
+++ b/internal/suites/suite_mariadb_test.go
@@ -7,11 +7,11 @@ import (
)
type MariadbSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewMariadbSuite() *MariadbSuite {
- return &MariadbSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &MariadbSuite{RodSuite: new(RodSuite)}
}
func (s *MariadbSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_mysql_test.go b/internal/suites/suite_mysql_test.go
index e6e6c6f7f..1a634ad27 100644
--- a/internal/suites/suite_mysql_test.go
+++ b/internal/suites/suite_mysql_test.go
@@ -7,11 +7,11 @@ import (
)
type MySQLSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewMySQLSuite() *MySQLSuite {
- return &MySQLSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &MySQLSuite{RodSuite: new(RodSuite)}
}
func (s *MySQLSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_network_acl_test.go b/internal/suites/suite_network_acl_test.go
index a8562a755..82b1e90d2 100644
--- a/internal/suites/suite_network_acl_test.go
+++ b/internal/suites/suite_network_acl_test.go
@@ -21,20 +21,21 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
- wds, err := StartWebDriver()
+ browser, err := StartRod()
s.Require().NoError(err)
defer func() {
- err = wds.Stop()
+ err = browser.WebDriver.Close()
s.Require().NoError(err)
+ browser.Launcher.Cleanup()
}()
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
- wds.doVisit(s.T(), targetURL)
- wds.verifyIsFirstFactorPage(ctx, s.T())
+ page := browser.doCreateTab(s.T(), targetURL).Context(ctx)
- wds.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, targetURL)
- wds.verifySecretAuthorized(ctx, s.T())
+ browser.verifyIsFirstFactorPage(s.T(), page)
+ browser.doRegisterAndLogin2FA(s.T(), page, "john", "password", false, targetURL)
+ browser.verifySecretAuthorized(s.T(), page)
}
// from network 192.168.240.201/32.
@@ -42,21 +43,22 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
- wds, err := StartWebDriverWithProxy("http://proxy-client1.example.com:3128", GetWebDriverPort())
+ browser, err := StartRodWithProxy("http://proxy-client1.example.com:3128")
s.Require().NoError(err)
defer func() {
- err = wds.Stop()
+ err = browser.WebDriver.Close()
s.Require().NoError(err)
+ browser.Launcher.Cleanup()
}()
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
- wds.doVisit(s.T(), targetURL)
- wds.verifyIsFirstFactorPage(ctx, s.T())
+ page := browser.doCreateTab(s.T(), targetURL).Context(ctx)
- wds.doLoginOneFactor(ctx, s.T(), "john", "password",
+ browser.verifyIsFirstFactorPage(s.T(), page)
+ browser.doLoginOneFactor(s.T(), page, "john", "password",
false, fmt.Sprintf("%s/secret.html", SecureBaseURL))
- wds.verifySecretAuthorized(ctx, s.T())
+ browser.verifySecretAuthorized(s.T(), page)
}
// from network 192.168.240.202/32.
@@ -64,16 +66,18 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon0FA() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
- wds, err := StartWebDriverWithProxy("http://proxy-client2.example.com:3128", GetWebDriverPort())
+ browser, err := StartRodWithProxy("http://proxy-client2.example.com:3128")
s.Require().NoError(err)
defer func() {
- err = wds.Stop()
+ err = browser.WebDriver.Close()
s.Require().NoError(err)
+ browser.Launcher.Cleanup()
}()
- wds.doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
- wds.verifySecretAuthorized(ctx, s.T())
+ page := browser.doCreateTab(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL)).Context(ctx)
+
+ browser.verifySecretAuthorized(s.T(), page)
}
func TestNetworkACLSuite(t *testing.T) {
diff --git a/internal/suites/suite_oidc_test.go b/internal/suites/suite_oidc_test.go
index 4c6326e0f..acf224dd8 100644
--- a/internal/suites/suite_oidc_test.go
+++ b/internal/suites/suite_oidc_test.go
@@ -7,11 +7,11 @@ import (
)
type OIDCSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewOIDCSuite() *OIDCSuite {
- return &OIDCSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &OIDCSuite{RodSuite: new(RodSuite)}
}
func (s *OIDCSuite) TestOIDCScenario() {
diff --git a/internal/suites/suite_oidc_traefik_test.go b/internal/suites/suite_oidc_traefik_test.go
index 38b8292ab..eae84c4d6 100644
--- a/internal/suites/suite_oidc_traefik_test.go
+++ b/internal/suites/suite_oidc_traefik_test.go
@@ -7,11 +7,11 @@ import (
)
type OIDCTraefikSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewOIDCTraefikSuite() *OIDCTraefikSuite {
- return &OIDCTraefikSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &OIDCTraefikSuite{RodSuite: new(RodSuite)}
}
func (s *OIDCTraefikSuite) TestOIDCScenario() {
diff --git a/internal/suites/suite_one_factor_only_test.go b/internal/suites/suite_one_factor_only_test.go
index 4b1971fd2..1588245e3 100644
--- a/internal/suites/suite_one_factor_only_test.go
+++ b/internal/suites/suite_one_factor_only_test.go
@@ -15,25 +15,25 @@ type OneFactorOnlySuite struct {
}
type OneFactorOnlyWebSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewOneFactorOnlyWebSuite() *OneFactorOnlyWebSuite {
- return &OneFactorOnlyWebSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &OneFactorOnlyWebSuite{RodSuite: new(RodSuite)}
}
func (s *OneFactorOnlyWebSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *OneFactorOnlyWebSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -41,62 +41,81 @@ func (s *OneFactorOnlyWebSuite) TearDownSuite() {
}
func (s *OneFactorOnlyWebSuite) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
+func (s *OneFactorOnlyWebSuite) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
// No target url is provided, then the user should be redirect to the default url.
func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURL() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsHome(s.T(), s.Context(ctx))
}
// Unsafe URL is provided, then the user should be redirect to the default url.
func (s *OneFactorOnlyWebSuite) TestShouldRedirectUserToDefaultURLWhenURLIsUnsafe() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "http://unsafe.local")
- s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "http://unsafe.local")
+ s.verifyIsHome(s.T(), s.Context(ctx))
}
// When use logged in and visit the portal again, she gets redirect to the authenticated view.
func (s *OneFactorOnlyWebSuite) TestShouldDisplayAuthenticatedView() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsAuthenticatedPage(ctx, s.T())
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
+
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsHome(s.T(), s.Context(ctx))
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsAuthenticatedPage(s.T(), s.Context(ctx))
}
func (s *OneFactorOnlyWebSuite) TestShouldRedirectAlreadyAuthenticatedUser() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsHome(s.T(), s.Context(ctx))
- s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://singlefactor.example.com:8080/secret.html", GetLoginBaseURL()))
- s.verifyURLIs(ctx, s.T(), "https://singlefactor.example.com:8080/secret.html")
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s?rd=https://singlefactor.example.com:8080/secret.html", GetLoginBaseURL()))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
+ s.verifyURLIs(s.T(), s.Context(ctx), "https://singlefactor.example.com:8080/secret.html")
}
func (s *OneFactorOnlyWebSuite) TestShouldNotRedirectAlreadyAuthenticatedUserToUnsafeURL() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- s.doLoginOneFactor(ctx, s.T(), "john", "password", false, "")
- s.verifyURLIs(ctx, s.T(), HomeBaseURL+"/")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), "john", "password", false, "")
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
- s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://secure.example.local:8080", GetLoginBaseURL()))
- s.verifyNotificationDisplayed(ctx, s.T(), "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.")
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s?rd=https://secure.example.local:8080", GetLoginBaseURL()))
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.")
}
func (s *OneFactorOnlySuite) TestWeb() {
diff --git a/internal/suites/suite_pathprefix_test.go b/internal/suites/suite_pathprefix_test.go
index e2dd8a534..098897afe 100644
--- a/internal/suites/suite_pathprefix_test.go
+++ b/internal/suites/suite_pathprefix_test.go
@@ -7,11 +7,11 @@ import (
)
type PathPrefixSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewPathPrefixSuite() *PathPrefixSuite {
- return &PathPrefixSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &PathPrefixSuite{RodSuite: new(RodSuite)}
}
func (s *PathPrefixSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_postgres_test.go b/internal/suites/suite_postgres_test.go
index 8ee97c2f4..616702f54 100644
--- a/internal/suites/suite_postgres_test.go
+++ b/internal/suites/suite_postgres_test.go
@@ -7,11 +7,11 @@ import (
)
type PostgresSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewPostgresSuite() *PostgresSuite {
- return &PostgresSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &PostgresSuite{RodSuite: new(RodSuite)}
}
func (s *PostgresSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suite_short_timeouts_test.go b/internal/suites/suite_short_timeouts_test.go
index 49df7ccaa..f59ebdce8 100644
--- a/internal/suites/suite_short_timeouts_test.go
+++ b/internal/suites/suite_short_timeouts_test.go
@@ -7,11 +7,11 @@ import (
)
type ShortTimeoutsSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewShortTimeoutsSuite() *ShortTimeoutsSuite {
- return &ShortTimeoutsSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &ShortTimeoutsSuite{RodSuite: new(RodSuite)}
}
func (s *ShortTimeoutsSuite) TestDefaultRedirectionURLScenario() {
diff --git a/internal/suites/suite_standalone_test.go b/internal/suites/suite_standalone_test.go
index 0049b8c56..6f2ce907c 100644
--- a/internal/suites/suite_standalone_test.go
+++ b/internal/suites/suite_standalone_test.go
@@ -18,25 +18,25 @@ import (
)
type StandaloneWebDriverSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewStandaloneWebDriverSuite() *StandaloneWebDriverSuite {
- return &StandaloneWebDriverSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &StandaloneWebDriverSuite{RodSuite: new(RodSuite)}
}
func (s *StandaloneWebDriverSuite) SetupSuite() {
- wds, err := StartWebDriver()
+ browser, err := StartRod()
if err != nil {
log.Fatal(err)
}
- s.WebDriverSession = wds
+ s.RodSession = browser
}
func (s *StandaloneWebDriverSuite) TearDownSuite() {
- err := s.WebDriverSession.Stop()
+ err := s.RodSession.Stop()
if err != nil {
log.Fatal(err)
@@ -44,62 +44,78 @@ func (s *StandaloneWebDriverSuite) TearDownSuite() {
}
func (s *StandaloneWebDriverSuite) SetupTest() {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+}
- s.doLogout(ctx, s.T())
- s.WebDriverSession.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+func (s *StandaloneWebDriverSuite) TearDownTest() {
+ s.collectCoverage(s.Page)
+ s.MustClose()
}
func (s *StandaloneWebDriverSuite) TestShouldLetUserKnowHeIsAlreadyAuthenticated() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- _ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
+ _ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
// Visit home page to change context.
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
- s.doVisit(s.T(), GetLoginBaseURL())
- s.verifyIsAuthenticatedPage(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), GetLoginBaseURL())
+ s.verifyIsAuthenticatedPage(s.T(), s.Context(ctx))
}
func (s *StandaloneWebDriverSuite) TestShouldRedirectAlreadyAuthenticatedUser() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- _ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
+ _ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
// Visit home page to change context.
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
- s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://secure.example.com:8080", GetLoginBaseURL()))
- s.verifyURLIs(ctx, s.T(), "https://secure.example.com:8080/")
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s?rd=https://secure.example.com:8080", GetLoginBaseURL()))
+
+ _, err := s.Page.ElementR("h1", "Public resource")
+ require.NoError(s.T(), err)
+ s.verifyURLIs(s.T(), s.Context(ctx), "https://secure.example.com:8080/")
}
func (s *StandaloneWebDriverSuite) TestShouldNotRedirectAlreadyAuthenticatedUserToUnsafeURL() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
- defer cancel()
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
- _ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
+ _ = s.doRegisterAndLogin2FA(s.T(), s.Context(ctx), "john", "password", false, "")
// Visit home page to change context.
- s.doVisit(s.T(), HomeBaseURL)
- s.verifyIsHome(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Context(ctx))
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
- s.doVisit(s.T(), fmt.Sprintf("%s?rd=https://secure.example.local:8080", GetLoginBaseURL()))
- s.verifyNotificationDisplayed(ctx, s.T(), "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.")
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s?rd=https://secure.example.local:8080", GetLoginBaseURL()))
+ s.verifyNotificationDisplayed(s.T(), s.Context(ctx), "Redirection was determined to be unsafe and aborted. Ensure the redirection URL is correct.")
}
func (s *StandaloneWebDriverSuite) TestShouldCheckUserIsAskedToRegisterDevice() {
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
- defer cancel()
+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer func() {
+ cancel()
+ s.collectScreenshot(ctx.Err(), s.Page)
+ }()
username := "john"
password := "password"
@@ -109,21 +125,21 @@ func (s *StandaloneWebDriverSuite) TestShouldCheckUserIsAskedToRegisterDevice()
require.NoError(s.T(), provider.DeleteTOTPSecret(username))
// Login one factor.
- s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), username, password, false, "")
// Check the user is asked to register a new device.
- s.WaitElementLocatedByClassName(ctx, s.T(), "state-not-registered")
+ s.WaitElementLocatedByClassName(s.T(), s.Context(ctx), "state-not-registered")
// Then register the TOTP factor.
- s.doRegisterTOTP(ctx, s.T())
+ s.doRegisterTOTP(s.T(), s.Context(ctx))
// And logout.
- s.doLogout(ctx, s.T())
+ s.doLogout(s.T(), s.Context(ctx))
// Login one factor again.
- s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
+ s.doLoginOneFactor(s.T(), s.Context(ctx), username, password, false, "")
// now the user should be asked to perform 2FA
- s.WaitElementLocatedByClassName(ctx, s.T(), "state-method")
+ s.WaitElementLocatedByClassName(s.T(), s.Context(ctx), "state-method")
}
type StandaloneSuite struct {
diff --git a/internal/suites/suite_traefik2_test.go b/internal/suites/suite_traefik2_test.go
index 0f7e36d75..0cbddf110 100644
--- a/internal/suites/suite_traefik2_test.go
+++ b/internal/suites/suite_traefik2_test.go
@@ -10,11 +10,11 @@ import (
)
type Traefik2Suite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewTraefik2Suite() *Traefik2Suite {
- return &Traefik2Suite{SeleniumSuite: new(SeleniumSuite)}
+ return &Traefik2Suite{RodSuite: new(RodSuite)}
}
func (s *Traefik2Suite) TestOneFactorScenario() {
@@ -30,31 +30,34 @@ func (s *Traefik2Suite) TestCustomHeaders() {
}
func (s *Traefik2Suite) TestShouldKeepSessionAfterRedisRestart() {
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
-
- wds, err := StartWebDriver()
- s.Require().NoError(err)
-
+ ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
defer func() {
- err = wds.Stop()
+ cancel()
+ s.collectCoverage(s.Page)
+ s.collectScreenshot(ctx.Err(), s.Page)
+ s.MustClose()
+ err := s.RodSession.Stop()
s.Require().NoError(err)
}()
- secret := wds.doRegisterThenLogout(ctx, s.T(), "john", "password")
+ browser, err := StartRod()
+ s.Require().NoError(err)
+ s.RodSession = browser
+
+ s.Page = s.doCreateTab(s.T(), HomeBaseURL)
+ s.verifyIsHome(s.T(), s.Page)
+ secret := s.doRegisterThenLogout(s.T(), s.Context(ctx), "john", "password")
- wds.doLoginTwoFactor(ctx, s.T(), "john", "password", false, secret, "")
+ s.doLoginTwoFactor(s.T(), s.Context(ctx), "john", "password", false, secret, "")
- wds.doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
- wds.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", SecureBaseURL))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
err = traefik2DockerEnvironment.Restart("redis")
s.Require().NoError(err)
- time.Sleep(5 * time.Second)
-
- wds.doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
- wds.verifySecretAuthorized(ctx, s.T())
+ s.doVisit(s.T(), s.Context(ctx), fmt.Sprintf("%s/secret.html", SecureBaseURL))
+ s.verifySecretAuthorized(s.T(), s.Context(ctx))
}
func TestTraefik2Suite(t *testing.T) {
diff --git a/internal/suites/suite_traefik_test.go b/internal/suites/suite_traefik_test.go
index 58147674e..3c1fbc7a5 100644
--- a/internal/suites/suite_traefik_test.go
+++ b/internal/suites/suite_traefik_test.go
@@ -7,11 +7,11 @@ import (
)
type TraefikSuite struct {
- *SeleniumSuite
+ *RodSuite
}
func NewTraefikSuite() *TraefikSuite {
- return &TraefikSuite{SeleniumSuite: new(SeleniumSuite)}
+ return &TraefikSuite{RodSuite: new(RodSuite)}
}
func (s *TraefikSuite) TestOneFactorScenario() {
diff --git a/internal/suites/suites.go b/internal/suites/suites.go
index c8f4c43a9..4595cf524 100644
--- a/internal/suites/suites.go
+++ b/internal/suites/suites.go
@@ -1,15 +1,16 @@
package suites
import (
+ "github.com/go-rod/rod"
"github.com/stretchr/testify/suite"
- "github.com/tebeka/selenium"
)
-// SeleniumSuite is a selenium suite.
-type SeleniumSuite struct {
+// RodSuite is a go-rod suite.
+type RodSuite struct {
suite.Suite
- *WebDriverSession
+ *RodSession
+ *rod.Page
}
// CommandSuite is a command line interface suite.
@@ -21,8 +22,3 @@ type CommandSuite struct {
*DockerEnvironment
}
-
-// WebDriver return the webdriver of the suite.
-func (s *SeleniumSuite) WebDriver() selenium.WebDriver {
- return s.WebDriverSession.WebDriver
-}
diff --git a/internal/suites/utils.go b/internal/suites/utils.go
index 6e26e87b8..d29e661db 100644
--- a/internal/suites/utils.go
+++ b/internal/suites/utils.go
@@ -1,11 +1,17 @@
package suites
import (
+ "context"
+ "fmt"
"io/ioutil"
+ "log"
"os"
"path/filepath"
- "strconv"
+ "runtime"
"strings"
+ "time"
+
+ "github.com/go-rod/rod"
)
// GetLoginBaseURL returns the URL of the login portal and the path prefix if specified.
@@ -17,16 +23,51 @@ func GetLoginBaseURL() string {
return LoginBaseURL
}
-// GetWebDriverPort returns the port to initialize the webdriver with.
-func GetWebDriverPort() int {
- driverPort := os.Getenv("CHROMEDRIVER_PORT")
- if driverPort == "" {
- driverPort = defaultChromeDriverPort
+func (rs *RodSession) collectCoverage(page *rod.Page) {
+ coverageDir := "../../web/.nyc_output"
+ now := time.Now()
+
+ resp, err := page.Eval("JSON.stringify(window.__coverage__)")
+ if err != nil {
+ log.Fatal(err)
}
- p, _ := strconv.Atoi(driverPort)
+ coverageData := fmt.Sprintf("%v", resp.Value)
- return p
+ _ = os.MkdirAll(coverageDir, 0775)
+
+ if coverageData != "<nil>" {
+ err = ioutil.WriteFile(fmt.Sprintf("%s/coverage-%d.json", coverageDir, now.Unix()), []byte(coverageData), 0664) //nolint:gosec
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = filepath.Walk("../../web/.nyc_output", fixCoveragePath)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (rs *RodSession) collectScreenshot(err error, page *rod.Page) {
+ if err == context.DeadlineExceeded && os.Getenv("CI") == stringTrue {
+ base := "/buildkite/screenshots"
+ build := os.Getenv("BUILDKITE_BUILD_NUMBER")
+ suite := strings.ToLower(os.Getenv("SUITE"))
+ job := os.Getenv("BUILDKITE_JOB_ID")
+ path := filepath.Join(fmt.Sprintf("%s/%s/%s/%s", base, build, suite, job)) //nolint: gocritic
+
+ if err := os.MkdirAll(path, 0755); err != nil {
+ log.Fatal(err)
+ }
+
+ pc, _, _, _ := runtime.Caller(2)
+ fn := runtime.FuncForPC(pc)
+ p := "github.com/authelia/authelia/v4/internal/suites."
+ r := strings.NewReplacer(p, "", "(", "", ")", "", "*", "", ".", "-")
+
+ page.MustScreenshotFullPage(fmt.Sprintf("%s/%s.jpg", path, r.Replace(fn.Name())))
+ }
}
func fixCoveragePath(path string, file os.FileInfo, err error) error {
diff --git a/internal/suites/verify_body_contains.go b/internal/suites/verify_body_contains.go
index 011e6366b..8594c2bb5 100644
--- a/internal/suites/verify_body_contains.go
+++ b/internal/suites/verify_body_contains.go
@@ -1,33 +1,29 @@
package suites
import (
- "context"
+ "fmt"
"strings"
"testing"
+ "github.com/go-rod/rod"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/tebeka/selenium"
)
-func (wds *WebDriverSession) verifyBodyContains(ctx context.Context, t *testing.T, pattern string) {
- err := wds.Wait(ctx, func(wd selenium.WebDriver) (bool, error) {
- bodyElement, err := wds.WebDriver.FindElement(selenium.ByTagName, "body")
+func (rs *RodSession) verifyBodyContains(t *testing.T, page *rod.Page, pattern string) {
+ body, err := page.Element("body")
+ assert.NoError(t, err)
+ assert.NotNil(t, body)
- if err != nil {
- return false, err
- }
+ text, err := body.Text()
+ assert.NoError(t, err)
+ assert.NotNil(t, text)
- if bodyElement == nil {
- return false, nil
- }
+ if strings.Contains(text, pattern) {
+ err = nil
+ } else {
+ err = fmt.Errorf("body does not contain pattern: %s", pattern)
+ }
- content, err := bodyElement.Text()
-
- if err != nil {
- return false, err
- }
-
- return strings.Contains(content, pattern), nil
- })
require.NoError(t, err)
}
diff --git a/internal/suites/verify_is_authenticated_page.go b/internal/suites/verify_is_authenticated_page.go
index abe01df76..5cb24d4b9 100644
--- a/internal/suites/verify_is_authenticated_page.go
+++ b/internal/suites/verify_is_authenticated_page.go
@@ -1,10 +1,11 @@
package suites
import (
- "context"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) verifyIsAuthenticatedPage(ctx context.Context, t *testing.T) {
- wds.WaitElementLocatedByID(ctx, t, "authenticated-stage")
+func (rs *RodSession) verifyIsAuthenticatedPage(t *testing.T, page *rod.Page) {
+ rs.WaitElementLocatedByCSSSelector(t, page, "authenticated-stage")
}
diff --git a/internal/suites/verify_is_consent_page.go b/internal/suites/verify_is_consent_page.go
index d737cfe10..de143a831 100644
--- a/internal/suites/verify_is_consent_page.go
+++ b/internal/suites/verify_is_consent_page.go
@@ -1,10 +1,11 @@
package suites
import (
- "context"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) verifyIsConsentPage(ctx context.Context, t *testing.T) {
- wds.WaitElementLocatedByID(ctx, t, "consent-stage")
+func (rs *RodSession) verifyIsConsentPage(t *testing.T, page *rod.Page) {
+ rs.WaitElementLocatedByCSSSelector(t, page, "consent-stage")
}
diff --git a/internal/suites/verify_is_first_factor_page.go b/internal/suites/verify_is_first_factor_page.go
index 3afe409c8..208194e30 100644
--- a/internal/suites/verify_is_first_factor_page.go
+++ b/internal/suites/verify_is_first_factor_page.go
@@ -1,10 +1,11 @@
package suites
import (
- "context"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) verifyIsFirstFactorPage(ctx context.Context, t *testing.T) {
- wds.WaitElementLocatedByID(ctx, t, "first-factor-stage")
+func (rs *RodSession) verifyIsFirstFactorPage(t *testing.T, page *rod.Page) {
+ rs.WaitElementLocatedByCSSSelector(t, page, "first-factor-stage")
}
diff --git a/internal/suites/verify_is_home.go b/internal/suites/verify_is_home.go
index c1bdedf01..a3fa2701e 100644
--- a/internal/suites/verify_is_home.go
+++ b/internal/suites/verify_is_home.go
@@ -1,11 +1,13 @@
package suites
import (
- "context"
"fmt"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) verifyIsHome(ctx context.Context, t *testing.T) {
- wds.verifyURLIs(ctx, t, fmt.Sprintf("%s/", HomeBaseURL))
+func (rs *RodSession) verifyIsHome(t *testing.T, page *rod.Page) {
+ page.MustElementR("h1", "Access the secret")
+ rs.verifyURLIs(t, page, fmt.Sprintf("%s/", HomeBaseURL))
}
diff --git a/internal/suites/verify_is_oidc.go b/internal/suites/verify_is_oidc.go
new file mode 100644
index 000000000..9e8e22197
--- /dev/null
+++ b/internal/suites/verify_is_oidc.go
@@ -0,0 +1,12 @@
+package suites
+
+import (
+ "testing"
+
+ "github.com/go-rod/rod"
+)
+
+func (rs *RodSession) verifyIsOIDC(t *testing.T, page *rod.Page, pattern, url string) {
+ page.MustElementR("body", pattern)
+ rs.verifyURLIs(t, page, url)
+}
diff --git a/internal/suites/verify_is_public.go b/internal/suites/verify_is_public.go
new file mode 100644
index 000000000..2a428d50f
--- /dev/null
+++ b/internal/suites/verify_is_public.go
@@ -0,0 +1,13 @@
+package suites
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/go-rod/rod"
+)
+
+func (rs *RodSession) verifyIsPublic(t *testing.T, page *rod.Page) {
+ page.MustElementR("body", "headers")
+ rs.verifyURLIs(t, page, fmt.Sprintf("%s/headers", PublicBaseURL))
+}
diff --git a/internal/suites/verify_is_second_factor_page.go b/internal/suites/verify_is_second_factor_page.go
index 8cc5c3259..a37ffc27e 100644
--- a/internal/suites/verify_is_second_factor_page.go
+++ b/internal/suites/verify_is_second_factor_page.go
@@ -1,10 +1,11 @@
package suites
import (
- "context"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) verifyIsSecondFactorPage(ctx context.Context, t *testing.T) {
- wds.WaitElementLocatedByID(ctx, t, "second-factor-stage")
+func (rs *RodSession) verifyIsSecondFactorPage(t *testing.T, page *rod.Page) {
+ rs.WaitElementLocatedByCSSSelector(t, page, "second-factor-stage")
}
diff --git a/internal/suites/verify_mail.go b/internal/suites/verify_mail.go
index 663594b8c..95cc046e7 100644
--- a/internal/suites/verify_mail.go
+++ b/internal/suites/verify_mail.go
@@ -1,10 +1,11 @@
package suites
import (
- "context"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) verifyMailNotificationDisplayed(ctx context.Context, t *testing.T) {
- wds.verifyNotificationDisplayed(ctx, t, "An email has been sent to your address to complete the process.")
+func (rs *RodSession) verifyMailNotificationDisplayed(t *testing.T, page *rod.Page) {
+ rs.verifyNotificationDisplayed(t, page, "An email has been sent to your address to complete the process.")
}
diff --git a/internal/suites/verify_notification.go b/internal/suites/verify_notification.go
index 26dba4792..262dfae13 100644
--- a/internal/suites/verify_notification.go
+++ b/internal/suites/verify_notification.go
@@ -1,14 +1,14 @@
package suites
import (
- "context"
"testing"
+ "github.com/go-rod/rod"
"github.com/stretchr/testify/assert"
)
-func (wds *WebDriverSession) verifyNotificationDisplayed(ctx context.Context, t *testing.T, message string) {
- el := wds.WaitElementLocatedByClassName(ctx, t, "notification")
+func (rs *RodSession) verifyNotificationDisplayed(t *testing.T, page *rod.Page, message string) {
+ el, err := page.ElementR(".notification", message)
+ assert.NoError(t, err)
assert.NotNil(t, el)
- wds.WaitElementTextContains(ctx, t, el, message)
}
diff --git a/internal/suites/verify_secret_authorized.go b/internal/suites/verify_secret_authorized.go
index 4859e128c..2c6c94f47 100644
--- a/internal/suites/verify_secret_authorized.go
+++ b/internal/suites/verify_secret_authorized.go
@@ -1,10 +1,11 @@
package suites
import (
- "context"
"testing"
+
+ "github.com/go-rod/rod"
)
-func (wds *WebDriverSession) verifySecretAuthorized(ctx context.Context, t *testing.T) {
- wds.WaitElementLocatedByID(ctx, t, "secret")
+func (rs *RodSession) verifySecretAuthorized(t *testing.T, page *rod.Page) {
+ rs.WaitElementLocatedByCSSSelector(t, page, "secret")
}
diff --git a/internal/suites/verify_url_is.go b/internal/suites/verify_url_is.go
index db3848b06..10c59c62f 100644
--- a/internal/suites/verify_url_is.go
+++ b/internal/suites/verify_url_is.go
@@ -1,23 +1,13 @@
package suites
import (
- "context"
"testing"
+ "github.com/go-rod/rod"
"github.com/stretchr/testify/require"
- "github.com/tebeka/selenium"
)
-func (wds *WebDriverSession) verifyURLIs(ctx context.Context, t *testing.T, url string) {
- err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
- currentURL, err := driver.CurrentURL()
-
- if err != nil {
- return false, err
- }
-
- return currentURL == url, nil
- })
-
- require.NoError(t, err)
+func (rs *RodSession) verifyURLIs(t *testing.T, page *rod.Page, url string) {
+ currentURL := page.MustInfo().URL
+ require.Equal(t, url, currentURL, "they should be equal")
}
diff --git a/internal/suites/webdriver.go b/internal/suites/webdriver.go
index 106207670..e93b23531 100644
--- a/internal/suites/webdriver.go
+++ b/internal/suites/webdriver.go
@@ -1,250 +1,115 @@
package suites
import (
- "context"
- "encoding/json"
- "errors"
"fmt"
- "io/ioutil"
"os"
- "path/filepath"
"strings"
"testing"
"time"
- log "github.com/sirupsen/logrus"
+ "github.com/go-rod/rod"
+ "github.com/go-rod/rod/lib/launcher"
"github.com/stretchr/testify/require"
- "github.com/tebeka/selenium"
- "github.com/tebeka/selenium/chrome"
)
-// WebDriverSession binding a selenium service and a webdriver.
-type WebDriverSession struct {
- service *selenium.Service
- WebDriver selenium.WebDriver
+// RodSession binding a chrome session with devtool protocol.
+type RodSession struct {
+ Launcher *launcher.Launcher
+ WebDriver *rod.Browser
}
-// StartWebDriverWithProxy create a selenium session.
-func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error) {
- driverPath := os.Getenv("CHROMEDRIVER_PATH")
- if driverPath == "" {
- driverPath = "/usr/bin/chromedriver"
- }
-
- service, err := selenium.NewChromeDriverService(driverPath, port)
-
- if err != nil {
- return nil, err
- }
-
+// StartRodWithProxy create a rod/chromedp session.
+func StartRodWithProxy(proxy string) (*RodSession, error) {
browserPath := os.Getenv("BROWSER_PATH")
if browserPath == "" {
browserPath = "/usr/bin/chromium-browser"
}
- chromeCaps := chrome.Capabilities{
- Path: browserPath,
- }
-
- chromeCaps.Args = append(chromeCaps.Args, "--ignore-certificate-errors")
+ headless := false
+ trace := true
+ motion := 0 * time.Second
if os.Getenv("HEADLESS") != "" {
- chromeCaps.Args = append(chromeCaps.Args, "--headless")
- chromeCaps.Args = append(chromeCaps.Args, "--no-sandbox")
+ headless = true
+ trace = false
+ motion = 0 * time.Second
}
- if proxy != "" {
- chromeCaps.Args = append(chromeCaps.Args, fmt.Sprintf("--proxy-server=%s", proxy))
- }
+ l := launcher.New().
+ Bin(browserPath).
+ Proxy(proxy).
+ Headless(headless).
+ Devtools(true)
+ url := l.MustLaunch()
- caps := selenium.Capabilities{}
- caps.AddChrome(chromeCaps)
+ browser := rod.New().
+ ControlURL(url).
+ Trace(trace).
+ SlowMotion(motion).
+ MustConnect()
- wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
- if err != nil {
- _ = service.Stop()
+ browser.MustIgnoreCertErrors(true)
- log.Fatal(err)
- }
-
- return &WebDriverSession{
- service: service,
- WebDriver: wd,
+ return &RodSession{
+ Launcher: l,
+ WebDriver: browser,
}, nil
}
-// StartWebDriver create a selenium session.
-func StartWebDriver() (*WebDriverSession, error) {
- return StartWebDriverWithProxy("", GetWebDriverPort())
-}
-
-// Stop stop the selenium session.
-func (wds *WebDriverSession) Stop() error {
- var coverage map[string]interface{}
-
- coverageDir := "../../web/.nyc_output"
- time := time.Now()
-
- resp, err := wds.WebDriver.ExecuteScriptRaw("return JSON.stringify(window.__coverage__)", nil)
- if err != nil {
- return err
- }
-
- err = json.Unmarshal(resp, &coverage)
- if err != nil {
- return err
- }
-
- coverageData := fmt.Sprintf("%s", coverage["value"])
-
- _ = os.MkdirAll(coverageDir, 0775)
-
- err = ioutil.WriteFile(fmt.Sprintf("%s/coverage-%d.json", coverageDir, time.Unix()), []byte(coverageData), 0664) //nolint:gosec
- if err != nil {
- return err
- }
-
- err = filepath.Walk("../../web/.nyc_output", fixCoveragePath)
- if err != nil {
- return err
- }
-
- err = wds.WebDriver.Quit()
- if err != nil {
- return err
- }
-
- return wds.service.Stop()
+// StartRod create a rod/chromedp session.
+func StartRod() (*RodSession, error) {
+ return StartRodWithProxy("")
}
-// WithWebdriver run some actions against a webdriver.
-func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error {
- wds, err := StartWebDriver()
-
+// Stop stop the rod/chromedp session.
+func (rs *RodSession) Stop() error {
+ err := rs.WebDriver.Close()
if err != nil {
return err
}
- defer wds.Stop() //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
-
- return fn(wds.WebDriver)
-}
-
-// Wait wait until condition holds true.
-func (wds *WebDriverSession) Wait(ctx context.Context, condition selenium.Condition) error {
- done := make(chan error, 1)
+ rs.Launcher.Cleanup()
- go func() {
- done <- wds.WebDriver.Wait(condition)
- }()
-
- select {
- case <-ctx.Done():
- return errors.New("waiting timeout reached")
- case err := <-done:
- return err
- }
+ return err
}
-func (wds *WebDriverSession) waitElementLocated(ctx context.Context, t *testing.T, by, value string) selenium.WebElement {
- var el selenium.WebElement
-
- err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
- var err error
- el, err = driver.FindElement(by, value)
-
- if err != nil {
- if strings.Contains(err.Error(), "no such element") {
- return false, nil
- }
- return false, err
- }
-
- return el != nil, nil
- })
-
+// WaitElementLocatedByClassName wait an element is located by class name.
+func (rs *RodSession) WaitElementLocatedByClassName(t *testing.T, page *rod.Page, className string) *rod.Element {
+ e, err := page.Element("." + className)
require.NoError(t, err)
- require.NotNil(t, el)
+ require.NotNil(t, e)
- return el
+ return e
}
-func (wds *WebDriverSession) waitElementsLocated(ctx context.Context, t *testing.T, by, value string) []selenium.WebElement {
- var el []selenium.WebElement
-
- err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
- var err error
- el, err = driver.FindElements(by, value)
-
- if err != nil {
- if strings.Contains(err.Error(), "no such element") {
- return false, nil
- }
- return false, err
- }
-
- return el != nil, nil
- })
-
+// WaitElementLocatedByCSSSelector wait an element is located by class name.
+func (rs *RodSession) WaitElementLocatedByCSSSelector(t *testing.T, page *rod.Page, cssSelector string) *rod.Element {
+ e, err := page.Element("#" + cssSelector)
require.NoError(t, err)
- require.NotNil(t, el)
+ require.NotNil(t, e)
- return el
-}
-
-// WaitElementLocatedByID wait an element is located by id.
-func (wds *WebDriverSession) WaitElementLocatedByID(ctx context.Context, t *testing.T, id string) selenium.WebElement {
- return wds.waitElementLocated(ctx, t, selenium.ByID, id)
-}
-
-// WaitElementLocatedByTagName wait an element is located by tag name.
-func (wds *WebDriverSession) WaitElementLocatedByTagName(ctx context.Context, t *testing.T, tagName string) selenium.WebElement {
- return wds.waitElementLocated(ctx, t, selenium.ByTagName, tagName)
-}
-
-// WaitElementLocatedByClassName wait an element is located by class name.
-func (wds *WebDriverSession) WaitElementLocatedByClassName(ctx context.Context, t *testing.T, className string) selenium.WebElement {
- return wds.waitElementLocated(ctx, t, selenium.ByClassName, className)
-}
-
-// WaitElementLocatedByLinkText wait an element is located by link text.
-func (wds *WebDriverSession) WaitElementLocatedByLinkText(ctx context.Context, t *testing.T, linkText string) selenium.WebElement {
- return wds.waitElementLocated(ctx, t, selenium.ByLinkText, linkText)
-}
-
-// WaitElementLocatedByCSSSelector wait an element is located by class name.
-func (wds *WebDriverSession) WaitElementLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) selenium.WebElement {
- return wds.waitElementLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
+ return e
}
// WaitElementsLocatedByCSSSelector wait an element is located by CSS selector.
-func (wds *WebDriverSession) WaitElementsLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) []selenium.WebElement {
- return wds.waitElementsLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
-}
-
-// WaitElementTextContains wait the text of an element contains a pattern.
-func (wds *WebDriverSession) WaitElementTextContains(ctx context.Context, t *testing.T, element selenium.WebElement, pattern string) {
- err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
- text, err := element.Text()
-
- if err != nil {
- return false, err
- }
-
- return strings.Contains(text, pattern), nil
- })
+func (rs *RodSession) WaitElementsLocatedByCSSSelector(t *testing.T, page *rod.Page, cssSelector string) rod.Elements {
+ e, err := page.Elements("#" + cssSelector)
require.NoError(t, err)
+ require.NotNil(t, e)
+
+ return e
}
-func (wds *WebDriverSession) waitBodyContains(ctx context.Context, t *testing.T, pattern string) {
- err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
- text, err := wds.WaitElementLocatedByTagName(ctx, t, "body").Text()
+func (rs *RodSession) waitBodyContains(t *testing.T, page *rod.Page, pattern string) {
+ text, err := page.MustElementR("body", pattern).Text()
+ require.NoError(t, err)
+ require.NotNil(t, text)
- if err != nil {
- return false, err
- }
+ if strings.Contains(text, pattern) {
+ err = nil
+ } else {
+ err = fmt.Errorf("body does not contain pattern: %s", pattern)
+ }
- return strings.Contains(text, pattern), nil
- })
require.NoError(t, err)
}
diff --git a/internal/utils/const.go b/internal/utils/const.go
index 21c1619b2..73ab546f3 100644
--- a/internal/utils/const.go
+++ b/internal/utils/const.go
@@ -33,7 +33,7 @@ const (
const (
// Hour is an int based representation of the time unit.
- Hour = time.Minute * 60
+ Hour = time.Minute * 60 //nolint: revive
// Day is an int based representation of the time unit.
Day = Hour * 24