Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rb] Add FedCM support to the ruby selenium client #13796

Draft
wants to merge 9 commits into
base: trunk
Choose a base branch
from
27 changes: 27 additions & 0 deletions rb/lib/selenium/webdriver/fedcm/account.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module FedCM
# Represents an account displayed in a FedCM account list.
# See: https://fedidcg.github.io/FedCM/#dictdef-identityprovideraccount
# https://fedidcg.github.io/FedCM/#webdriver-accountlist
class Account
LOGIN_STATE_SIGNIN = "SignIn"
LOGIN_STATE_SIGNUP = "SignUp"

attr_reader :account_id, :email, :name, :given_name, :picture_url,
:idp_config_url, :login_state, :terms_of_service_url, :privacy_policy_url

# Initializes a new account with the provided attributes.
#
# @param [Hash]
def initialize(**args)
@account_id = args[:accountId]
@email = args[:email]
@name = args[:name]
@given_name = args[:givenName]
@picture_url = args[:pictureUrl]
@idp_config_url = args[:idpConfigUrl]
@login_state = args[:loginState]
@terms_of_service_url = args[:termsOfServiceUrl]
@privacy_policy_url = args[:privacyPolicyUrl]
end
end
end
42 changes: 42 additions & 0 deletions rb/lib/selenium/webdriver/fedcm/dialog.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module FedCM
module Dialog
DIALOG_TYPE_ACCOUNT_LIST = "AccountChooser".freeze
DIALOG_TYPE_AUTO_REAUTH = "AutoReauthn".freeze

# Closes the dialog as if the user had clicked X.
def cancel
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# Selects an account as if the user had clicked on it.
#
# @param [Integer] index The index of the account to select from the list returned by get_accounts.
def select_account(index)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# Returns the type of the open dialog.
#
# One of DIALOG_TYPE_ACCOUNT_LIST and DIALOG_TYPE_AUTO_REAUTH.
def type
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# Returns the title of the dialog.
def title
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# Returns the subtitle of the dialog or nil if none.
def subtitle
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# Returns the accounts shown in the account chooser.
#
# If this is an auto reauth dialog, returns the single account that is being signed in.
def accounts
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module HasFederatedCredentialManagement
def execute_fedcm(cmd, **params)
@bridge.send_command(cmd: cmd, params: params)
end
# Disables the promise rejection delay for FedCM.
#
# FedCM by default delays promise resolution in failure cases for privacy reasons.
# This method allows turning it off to let tests run faster where this is not relevant.
def set_delay_enabled(enabled)
# Implementation to disable the delay
# Placeholder for actual code to interact with browser APIs or other integrations.
end

# Resets the FedCM dialog cooldown.
#
# If a user agent triggers a cooldown when the account chooser is dismissed,
# this method resets that cooldown so that the dialog can be triggered again immediately.
def reset_cooldown
# Implementation to reset the cooldown
# Placeholder for actual code to interact with browser APIs or other integrations.
end

# Gets the currently open FedCM dialog, or nil if there is no dialog.
#
# This can be used similar to WebDriverWait in Selenium to wait until a dialog appears.
def federated_credential_management_dialog
# Implementation to retrieve the dialog
# Placeholder for actual code to check the dialog's presence.
end
end
51 changes: 51 additions & 0 deletions rb/sig/lib/selenium/webdriver/fedcm/account.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
module FedCM
# Represents an account displayed in a FedCM account list.
# See: https://fedidcg.github.io/FedCM/#dictdef-identityprovideraccount
# https://fedidcg.github.io/FedCM/#webdriver-accountlist
class Account
@account_id: untyped

@email: untyped

@name: untyped

@given_name: untyped

@picture_url: untyped

@idp_config_url: untyped

@login_state: untyped

@terms_of_service_url: untyped

@privacy_policy_url: untyped

LOGIN_STATE_SIGNIN: "SignIn"

LOGIN_STATE_SIGNUP: "SignUp"

attr_reader account_id: untyped

attr_reader email: untyped

attr_reader name: untyped

attr_reader given_name: untyped

attr_reader picture_url: untyped

attr_reader idp_config_url: untyped

attr_reader login_state: untyped

attr_reader terms_of_service_url: untyped

attr_reader privacy_policy_url: untyped

# Initializes a new account with the provided attributes.
#
# @param [Hash]
def initialize: (**untyped args) -> void
end
end
31 changes: 31 additions & 0 deletions rb/sig/lib/selenium/webdriver/fedcm/dialog.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module FedCM
module Dialog
DIALOG_TYPE_ACCOUNT_LIST: "AccountChooser"

DIALOG_TYPE_AUTO_REAUTH: "AutoReauthn"

# Closes the dialog as if the user had clicked X.
def cancel: () -> untyped

# Selects an account as if the user had clicked on it.
#
# @param [Integer] index The index of the account to select from the list returned by get_accounts.
def select_account: (untyped index) -> untyped

# Returns the type of the open dialog.
#
# One of DIALOG_TYPE_ACCOUNT_LIST and DIALOG_TYPE_AUTO_REAUTH.
def type: () -> untyped

# Returns the title of the dialog.
def title: () -> untyped

# Returns the subtitle of the dialog or nil if none.
def subtitle: () -> untyped

# Returns the accounts shown in the account chooser.
#
# If this is an auto reauth dialog, returns the single account that is being signed in.
def accounts: () -> untyped
end
end
64 changes: 64 additions & 0 deletions rb/spec/unit/selenium/webdriver/fedcm/fedcm_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require File.expand_path('../spec_helper', __dir__)

describe 'FederatedCredentialManagementTest' do
let(:options) do
Selenium::WebDriver::Chrome::Options.new.tap do |opts|
opts.add_argument("host-resolver-rules=MAP localhost:443 localhost:#{get_secure_port}")
opts.add_argument('ignore-certificate-errors')
end
end

let(:driver) { Selenium::WebDriver.for :chrome, options: options }
let(:app_server) { 'localhost' }

before do
driver.navigate.to "https://#{app_server}/fedcm/fedcm.html"
end

def trigger_fed_cm
driver.execute_script('triggerFedCm()')
end

def wait_for_dialog
wait = Selenium::WebDriver::Wait.new(timeout: 5)
wait.until { !driver.federated_credential_management_dialog }
end

def get_secure_port
uri = URI("https://#{app_server}/")
uri.port
rescue URI::InvalidURIError
0 # This should not happen
end

it 'dismisses dialog' do
fedcm_driver.set_delay_enabled(false)
expect(fedcm_driver.federated_credential_management_dialog).to be_nil

wait_for_dialog

dialog = fedcm_driver.federated_credential_management_dialog
expect(dialog.title).to eq("Sign in to localhost with localhost")
expect(dialog.dialog_type).to eq("AccountChooser")

dialog.cancel_dialog

expect { js_driver.execute_script('await promise') }.to raise_error(Selenium::WebDriver::Error::JavascriptError)
end

it 'selects an account' do
fedcm_driver.set_delay_enabled(false)
expect(fedcm_driver.federated_credential_management_dialog).to be_nil

wait_for_dialog

dialog = fedcm_driver.federated_credential_management_dialog
expect(dialog.title).to eq("Sign in to localhost with localhost")
expect(dialog.dialog_type).to eq("AccountChooser")

dialog.select_account(0)

response = driver.execute_script('return await promise')
expect(response).to include("token" => "a token")
end
end