Skip to content

Commit 8d027c5

Browse files
MarcialRosalesmergify[bot]
authored andcommitted
Make end_session_endpoint configurable
(cherry picked from commit 72ab194) # Conflicts: # deps/oauth2_client/src/oauth2_client.erl
1 parent cae6c78 commit 8d027c5

File tree

14 files changed

+278
-107
lines changed

14 files changed

+278
-107
lines changed

deps/oauth2_client/include/oauth2_client.hrl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
-define(RESPONSE_ISSUER, <<"issuer">>).
4242
-define(RESPONSE_TOKEN_ENDPOINT, <<"token_endpoint">>).
4343
-define(RESPONSE_AUTHORIZATION_ENDPOINT, <<"authorization_endpoint">>).
44+
-define(RESPONSE_END_SESSION_ENDPOINT, <<"end_session_endpoint">>).
4445
-define(RESPONSE_JWKS_URI, <<"jwks_uri">>).
4546
-define(RESPONSE_TLS_OPTIONS, <<"ssl_options">>).
4647

@@ -51,6 +52,7 @@
5152
issuer :: option(uri_string:uri_string()),
5253
token_endpoint :: option(uri_string:uri_string()),
5354
authorization_endpoint :: option(uri_string:uri_string()),
55+
end_session_endpoint :: option(uri_string:uri_string()),
5456
jwks_uri :: option(uri_string:uri_string()),
5557
ssl_options :: option(list())
5658
}).

deps/oauth2_client/src/oauth2_client.erl

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,19 @@
77
-module(oauth2_client).
88
-export([get_access_token/2, get_expiration_time/1,
99
refresh_access_token/2,
10-
get_oauth_provider/1, get_oauth_provider/2,
10+
get_oauth_provider/1, get_oauth_provider/2,
1111
extract_ssl_options_as_list/1
1212
]).
1313

1414
-include("oauth2_client.hrl").
15+
<<<<<<< HEAD
1516
-include_lib("public_key/include/public_key.hrl").
1617

1718
-spec get_access_token(oauth_provider_id() | oauth_provider(), access_token_request()) ->
19+
=======
20+
-spec get_access_token(oauth_provider(), access_token_request()) ->
21+
>>>>>>> 72ab1944bf (Make end_session_endpoint configurable)
1822
{ok, successful_access_token_response()} | {error, unsuccessful_access_token_response() | any()}.
19-
get_access_token(OAuth2ProviderId, Request) when is_binary(OAuth2ProviderId) ->
20-
rabbit_log:debug("get_access_token using OAuth2ProviderId:~p and client_id:~p",
21-
[OAuth2ProviderId, Request#access_token_request.client_id]),
22-
case get_oauth_provider(OAuth2ProviderId, [token_endpoint]) of
23-
{error, _Error } = Error0 -> Error0;
24-
{ok, Provider} -> get_access_token(Provider, Request)
25-
end;
26-
2723
get_access_token(OAuthProvider, Request) ->
2824
rabbit_log:debug("get_access_token using OAuthProvider:~p and client_id:~p",
2925
[OAuthProvider, Request#access_token_request.client_id]),
@@ -104,14 +100,20 @@ do_update_oauth_provider_endpoints_configuration(OAuthProvider) ->
104100
case OAuthProvider#oauth_provider.token_endpoint of
105101
undefined ->
106102
do_nothing;
107-
TokenEndPoint ->
108-
application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, TokenEndPoint)
103+
TokenEndpoint ->
104+
application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, TokenEndpoint)
109105
end,
110106
case OAuthProvider#oauth_provider.authorization_endpoint of
111107
undefined ->
112108
do_nothing;
113-
AuthzEndPoint ->
114-
application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, AuthzEndPoint)
109+
AuthzEndpoint ->
110+
application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, AuthzEndpoint)
111+
end,
112+
case OAuthProvider#oauth_provider.end_session_endpoint of
113+
undefined ->
114+
do_nothing;
115+
EndSessionEndpoint ->
116+
application:set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, EndSessionEndpoint)
115117
end,
116118
List = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []),
117119
ModifiedList = case OAuthProvider#oauth_provider.jwks_uri of
@@ -127,17 +129,21 @@ do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider)
127129
LookupProviderPropList = maps:get(OAuthProviderId, OAuthProviders),
128130
ModifiedList0 = case OAuthProvider#oauth_provider.token_endpoint of
129131
undefined -> LookupProviderPropList;
130-
TokenEndPoint -> [{token_endpoint, TokenEndPoint} | LookupProviderPropList]
132+
TokenEndpoint -> [{token_endpoint, TokenEndpoint} | LookupProviderPropList]
131133
end,
132134
ModifiedList1 = case OAuthProvider#oauth_provider.authorization_endpoint of
133135
undefined -> ModifiedList0;
134-
AuthzEndPoint -> [{authorization_endpoint, AuthzEndPoint} | ModifiedList0]
136+
AuthzEndpoint -> [{authorization_endpoint, AuthzEndpoint} | ModifiedList0]
135137
end,
136-
ModifiedList2 = case OAuthProvider#oauth_provider.jwks_uri of
138+
ModifiedList2 = case OAuthProvider#oauth_provider.end_session_endpoint of
137139
undefined -> ModifiedList1;
138-
JwksEndPoint -> [{jwks_uri, JwksEndPoint} | ModifiedList1]
140+
EndSessionEndpoint -> [{end_session_endpoint, EndSessionEndpoint} | ModifiedList1]
141+
end,
142+
ModifiedList3 = case OAuthProvider#oauth_provider.jwks_uri of
143+
undefined -> ModifiedList2;
144+
JwksEndPoint -> [{jwks_uri, JwksEndPoint} | ModifiedList2]
139145
end,
140-
ModifiedOAuthProviders = maps:put(OAuthProviderId, ModifiedList2, OAuthProviders),
146+
ModifiedOAuthProviders = maps:put(OAuthProviderId, ModifiedList3, OAuthProviders),
141147
application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, ModifiedOAuthProviders),
142148
rabbit_log:debug("Replacing oauth_providers ~p", [ ModifiedOAuthProviders]),
143149
OAuthProvider.
@@ -285,11 +291,15 @@ find_missing_attributes(#oauth_provider{} = OAuthProvider, RequiredAttributes) -
285291
lookup_oauth_provider_from_keyconfig() ->
286292
Issuer = application:get_env(rabbitmq_auth_backend_oauth2, issuer, undefined),
287293
TokenEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, token_endpoint, undefined),
294+
AuthorizationEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, undefined),
295+
EndSessionEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, undefined),
288296
Map = maps:from_list(application:get_env(rabbitmq_auth_backend_oauth2, key_config, [])),
289297
#oauth_provider{
290298
issuer = Issuer,
291299
jwks_uri = maps:get(jwks_url, Map, undefined), %% jwks_url not uri . _url is the legacy name
292300
token_endpoint = TokenEndpoint,
301+
authorization_endpoint = AuthorizationEndpoint,
302+
end_session_endpoint = EndSessionEndpoint,
293303
ssl_options = extract_ssl_options_as_list(Map)
294304
}.
295305

@@ -447,6 +457,7 @@ map_to_oauth_provider(Map) when is_map(Map) ->
447457
issuer = maps:get(?RESPONSE_ISSUER, Map),
448458
token_endpoint = maps:get(?RESPONSE_TOKEN_ENDPOINT, Map, undefined),
449459
authorization_endpoint = maps:get(?RESPONSE_AUTHORIZATION_ENDPOINT, Map, undefined),
460+
end_session_endpoint = maps:get(?RESPONSE_END_SESSION_ENDPOINT, Map, undefined),
450461
jwks_uri = maps:get(?RESPONSE_JWKS_URI, Map, undefined)
451462
};
452463

@@ -455,6 +466,7 @@ map_to_oauth_provider(PropList) when is_list(PropList) ->
455466
issuer = proplists:get_value(issuer, PropList),
456467
token_endpoint = proplists:get_value(token_endpoint, PropList),
457468
authorization_endpoint = proplists:get_value(authorization_endpoint, PropList, undefined),
469+
end_session_endpoint = proplists:get_value(end_session_endpoint, PropList, undefined),
458470
jwks_uri = proplists:get_value(jwks_uri, PropList, undefined),
459471
ssl_options = extract_ssl_options_as_list(maps:from_list(proplists:get_value(https, PropList, [])))
460472
}.

deps/oauth2_client/test/system_SUITE.erl

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
{issuer, build_issuer("http") },
101101
{authorization_endpoint, <<"http://localhost:8000/authorize">>},
102102
{token_endpoint, build_token_endpoint_uri("http")},
103+
{end_session_endpoint, <<"http://localhost:8000/logout">>},
103104
{jwks_uri, build_jwks_uri("http")}
104105
]}
105106
]
@@ -117,6 +118,7 @@
117118
{issuer, build_issuer("https") },
118119
{authorization_endpoint, <<"https://localhost:8000/authorize">>},
119120
{token_endpoint, build_token_endpoint_uri("https")},
121+
{end_session_endpoint, <<"http://localhost:8000/logout">>},
120122
{jwks_uri, build_jwks_uri("https")}
121123
]}
122124
]
@@ -166,8 +168,7 @@ groups() ->
166168
denies_access_token,
167169
auth_server_error,
168170
non_json_payload,
169-
grants_refresh_token,
170-
grants_access_token_using_oauth_provider_id
171+
grants_refresh_token
171172
]},
172173
{verify_get_oauth_provider, [], [
173174
get_oauth_provider,
@@ -262,6 +263,8 @@ configure_all_oauth_provider_settings(Config) ->
262263
application:set_env(rabbitmq_auth_backend_oauth2, issuer, OAuthProvider#oauth_provider.issuer),
263264
application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders),
264265
application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, OAuthProvider#oauth_provider.token_endpoint),
266+
application:set_env(rabbitmq_auth_backend_oauth2, end_sessione_endpoint, OAuthProvider#oauth_provider.end_session_endpoint),
267+
application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, OAuthProvider#oauth_provider.authorization_endpoint),
265268
KeyConfig = [ { jwks_url, OAuthProvider#oauth_provider.jwks_uri } ] ++
266269
case OAuthProvider#oauth_provider.ssl_options of
267270
undefined ->
@@ -313,6 +316,8 @@ end_per_testcase(_, Config) ->
313316
application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers),
314317
application:unset_env(rabbitmq_auth_backend_oauth2, issuer),
315318
application:unset_env(rabbitmq_auth_backend_oauth2, token_endpoint),
319+
application:unset_env(rabbitmq_auth_backend_oauth2, authorization_endpoint),
320+
application:unset_env(rabbitmq_auth_backend_oauth2, end_session_endpoint),
316321
application:unset_env(rabbitmq_auth_backend_oauth2, key_config),
317322
case ?config(group, Config) of
318323
http_up ->
@@ -340,15 +345,6 @@ grants_access_token_dynamically_resolving_oauth_provider(Config) ->
340345
?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType),
341346
?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken).
342347

343-
grants_access_token_using_oauth_provider_id(Config) ->
344-
#{request := #{parameters := Parameters},
345-
response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] } = lookup_expectation(token_endpoint, Config),
346-
347-
{ok, #successful_access_token_response{access_token = AccessToken, token_type = TokenType} } =
348-
oauth2_client:get_access_token(?config(oauth_provider_id, Config), build_access_token_request(Parameters)),
349-
?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType),
350-
?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken).
351-
352348
grants_access_token(Config) ->
353349
#{request := #{parameters := Parameters},
354350
response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] }
@@ -417,11 +413,19 @@ get_oauth_provider_given_oauth_provider_id(Config) ->
417413
= lookup_expectation(get_openid_configuration, Config),
418414

419415
ct:log("get_oauth_provider ~p", [?config(oauth_provider_id, Config)]),
420-
{ok, #oauth_provider{issuer = Issuer, token_endpoint = TokenEndPoint, jwks_uri = Jwks_uri}} =
421-
oauth2_client:get_oauth_provider(?config(oauth_provider_id, Config), [issuer, token_endpoint, jwks_uri]),
416+
{ok, #oauth_provider{
417+
issuer = Issuer,
418+
token_endpoint = TokenEndPoint,
419+
authorization_endpoint = AuthorizationEndpoint,
420+
end_session_endpoint = EndSessionEndpoint,
421+
jwks_uri = Jwks_uri}} =
422+
oauth2_client:get_oauth_provider(?config(oauth_provider_id, Config),
423+
[issuer, token_endpoint, jwks_uri, authorization_endpoint, end_session_endpoint]),
422424

423425
?assertEqual(proplists:get_value(issuer, JsonPayload), Issuer),
424426
?assertEqual(proplists:get_value(token_endpoint, JsonPayload), TokenEndPoint),
427+
?assertEqual(proplists:get_value(authorization_endpoint, JsonPayload), AuthorizationEndpoint),
428+
?assertEqual(proplists:get_value(end_session_endpoint, JsonPayload), EndSessionEndpoint),
425429
?assertEqual(proplists:get_value(jwks_uri, JsonPayload), Jwks_uri).
426430

427431

deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@
163163
"rabbitmq_auth_backend_oauth2.key_config.jwks_url",
164164
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
165165

166+
{mapping,
167+
"auth_oauth2.end_session_endpoint",
168+
"rabbitmq_auth_backend_oauth2.end_session_endpoint",
169+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
170+
171+
{mapping,
172+
"auth_oauth2.authorization_endpoint",
173+
"rabbitmq_auth_backend_oauth2.authorization_endpoint",
174+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
175+
166176
{mapping,
167177
"auth_oauth2.https.peer_verification",
168178
"rabbitmq_auth_backend_oauth2.key_config.peer_verification",
@@ -240,6 +250,16 @@
240250
[{datatype, string}, {validators, ["uri", "https_uri"]}]
241251
}.
242252

253+
{mapping,
254+
"auth_oauth2.oauth_providers.$name.end_session_endpoint",
255+
"rabbitmq_auth_backend_oauth2.oauth_providers",
256+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
257+
258+
{mapping,
259+
"auth_oauth2.oauth_providers.$name.authorization_endpoint",
260+
"rabbitmq_auth_backend_oauth2.oauth_providers",
261+
[{datatype, string}, {validators, ["uri", "https_uri"]}]}.
262+
243263
{mapping,
244264
"auth_oauth2.oauth_providers.$name.https.verify",
245265
"rabbitmq_auth_backend_oauth2.oauth_providers",

deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@
129129
auth_oauth2.oauth_providers.uaa.issuer = https://uaa
130130
auth_oauth2.oauth_providers.keycloak.token_endpoint = https://keycloak/token
131131
auth_oauth2.oauth_providers.keycloak.jwks_uri = https://keycloak/keys
132+
auth_oauth2.oauth_providers.keycloak.authorization_endpoint = https://keycloak/authorize
133+
auth_oauth2.oauth_providers.keycloak.end_session_endpoint = https://keycloak/logout
132134
auth_oauth2.oauth_providers.keycloak.https.cacertfile = /mnt/certs/ca_certificate.pem
133135
auth_oauth2.oauth_providers.keycloak.https.verify = verify_none",
134136
[
@@ -149,8 +151,10 @@
149151
{verify, verify_none},
150152
{cacertfile, "/mnt/certs/ca_certificate.pem"}
151153
]},
154+
{end_session_endpoint, <<"https://keycloak/logout">>},
155+
{authorization_endpoint, <<"https://keycloak/authorize">>},
152156
{token_endpoint, <<"https://keycloak/token">>},
153-
{jwks_uri, <<"https://keycloak/keys">>}
157+
{jwks_uri, <<"https://keycloak/keys">>}
154158
]
155159

156160
}

deps/rabbitmq_management/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ define PROJECT_APP_EXTRA_KEYS
2222
endef
2323

2424
DEPS = rabbit_common rabbit amqp_client cowboy cowlib rabbitmq_web_dispatch rabbitmq_management_agent oauth2_client
25-
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper
25+
TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper amqp10_client
2626
LOCAL_DEPS += ranch ssl crypto public_key
2727

2828
# FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked.

deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ function oauth_initialize_user_manager(resource_server) {
103103
audience: resource_server.id, // required by oauth0
104104
},
105105
};
106+
if (resource_server.end_session_endpoint != "") {
107+
oidcSettings.metadataSeed = {
108+
end_session_endpoint: resource_server.end_session_endpoint
109+
}
110+
}
106111
if (resource_server.oauth_client_secret != "") {
107112
oidcSettings.client_secret = resource_server.oauth_client_secret;
108113
}

deps/rabbitmq_management/selenium/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"description": "",
55
"main": "index.js",
66
"scripts": {
7-
"test": "mocha --recursive --trace-warnings --timeout 45000",
87
"fakeportal": "node fakeportal/app.js",
98
"fakeproxy": "node fakeportal/proxy.js",
109
"amqp10_roundtriptest": "eval $(cat $ENV_FILE ) &&./run-amqp10-roundtriptest",

deps/rabbitmq_management/selenium/test/basic-auth/ac-management.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('management user with vhosts permissions', function () {
4949
await overview.waitForQueuesTab()
5050
assert.ok(!await overview.isPopupWarningDisplayed())
5151
})
52-
it('can access limited options in admin tab', async function () {
52+
it('can access limited options in admin tab', async function () {
5353
await overview.clickOnAdminTab()
5454
await overview.waitForAdminTab()
5555
assert.ok(!await overview.isPopupWarningDisplayed())
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export PUBLIC_RABBITMQ_HOST=proxy:9090
22
export RABBITMQ_HOST_FOR_PROXY=rabbitmq:15672
3+
export SELENIUM_INTERACTION_DELAY=250
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export PUBLIC_RABBITMQ_HOST=localhost:9090
22
export RABBITMQ_HOST_FOR_PROXY=host.docker.internal:15672
3+
export SELENIUM_INTERACTION_DELAY=250

deps/rabbitmq_management/selenium/test/pageobjects/BasePage.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ module.exports = class BasePage {
1717
driver
1818
timeout
1919
polling
20+
interactionDelay
2021

2122
constructor (webdriver) {
2223
this.driver = webdriver
2324
this.timeout = parseInt(process.env.SELENIUM_TIMEOUT) || 1000 // max time waiting to locate an element. Should be less that test timeout
2425
this.polling = parseInt(process.env.SELENIUM_POLLING) || 500 // how frequent selenium searches for an element
26+
this.interactionDelay = parseInt(process.env.SELENIUM_INTERACTION_DELAY) || 0 // slow down interactions (when rabbit is behind a http proxy)
27+
console.log("Interaction Delay : " + this.interactionDelay)
2528
}
2629

2730

@@ -54,10 +57,10 @@ module.exports = class BasePage {
5457
return this.waitForDisplayed(CONNECTIONS_TAB)
5558
}
5659

57-
async clickOnAdminTab () {
60+
async clickOnAdminTab () {
5861
return this.click(ADMIN_TAB)
5962
}
60-
async waitForAdminTab() {
63+
async waitForAdminTab() {
6164
return this.waitForDisplayed(ADMIN_TAB)
6265
}
6366

@@ -71,18 +74,19 @@ module.exports = class BasePage {
7174
async clickOnExchangesTab () {
7275
return this.click(EXCHANGES_TAB)
7376
}
74-
async waitForExchangesTab() {
77+
async waitForExchangesTab() {
7578
return this.waitForDisplayed(EXCHANGES_TAB)
7679
}
7780

7881
async clickOnQueuesTab () {
7982
return this.click(QUEUES_AND_STREAMS_TAB)
8083
}
8184
async waitForQueuesTab() {
85+
await this.driver.sleep(250)
8286
return this.waitForDisplayed(QUEUES_AND_STREAMS_TAB)
8387
}
8488

85-
async clickOnStreamTab () {
89+
async clickOnStreamTab () {
8690
return this.click(STREAM_CONNECTIONS_TAB)
8791
}
8892
async waitForStreamConnectionsTab() {
@@ -153,8 +157,8 @@ module.exports = class BasePage {
153157
async isDisplayed(locator) {
154158
try {
155159
element = await driver.findElement(locator)
156-
console.log("element:"+element)
157-
return this.driver.wait(until.elementIsVisible(element), this.timeout / 2,
160+
161+
return this.driver.wait(until.elementIsVisible(element), this.timeout,
158162
'Timed out after [timeout=' + this.timeout + ';polling=' + this.polling + '] awaiting till visible ' + element,
159163
this.polling / 2)
160164
}catch(error) {
@@ -186,7 +190,13 @@ module.exports = class BasePage {
186190

187191

188192
async waitForDisplayed (locator) {
189-
return this.waitForVisible(await this.waitForLocated(locator))
193+
if (this.interactionDelay && this.interactionDelay > 0) await this.driver.sleep(this.interactionDelay)
194+
try {
195+
return this.waitForVisible(await this.waitForLocated(locator))
196+
}catch(error) {
197+
console.error("Failed to waitForDisplayed for locator " + locator)
198+
throw error
199+
}
190200
}
191201

192202
async getText (locator) {
@@ -200,6 +210,8 @@ module.exports = class BasePage {
200210
}
201211

202212
async click (locator) {
213+
if (this.interactionDelay) await this.driver.sleep(this.interactionDelay)
214+
203215
const element = await this.waitForDisplayed(locator)
204216
try {
205217
return element.click()

0 commit comments

Comments
 (0)