Login with Google (#15986)
This commit is contained in:
parent
681ab3aac2
commit
629c7114da
26 changed files with 250 additions and 13 deletions
1
Gemfile
1
Gemfile
|
|
@ -63,6 +63,7 @@ gem "omniauth", "~> 2.0" # A generalized Rack framework for multiple-provider au
|
|||
gem "omniauth-apple", "~> 1.0" # OmniAuth strategy for Sign In with Apple
|
||||
gem "omniauth-facebook", "~> 9.0" # OmniAuth strategy for Facebook
|
||||
gem "omniauth-github", "~> 2.0" # OmniAuth strategy for GitHub
|
||||
gem "omniauth-google-oauth2", "~> 1.0"
|
||||
gem "omniauth-rails_csrf_protection", "~> 1.0" # Provides CSRF protection on OmniAuth request endpoint on Rails application.
|
||||
gem "omniauth-twitter", "~> 1.4" # OmniAuth strategy for Twitter
|
||||
gem "parallel", "~> 1.21" # Run any kind of code in parallel processes
|
||||
|
|
|
|||
|
|
@ -523,6 +523,11 @@ GEM
|
|||
omniauth-github (2.0.0)
|
||||
omniauth (~> 2.0)
|
||||
omniauth-oauth2 (~> 1.7.1)
|
||||
omniauth-google-oauth2 (1.0.0)
|
||||
jwt (>= 2.0)
|
||||
oauth2 (~> 1.1)
|
||||
omniauth (~> 2.0)
|
||||
omniauth-oauth2 (~> 1.7.1)
|
||||
omniauth-oauth (1.2.0)
|
||||
oauth
|
||||
omniauth (>= 1.0, < 3)
|
||||
|
|
@ -966,6 +971,7 @@ DEPENDENCIES
|
|||
omniauth-apple (~> 1.0)
|
||||
omniauth-facebook (~> 9.0)
|
||||
omniauth-github (~> 2.0)
|
||||
omniauth-google-oauth2 (~> 1.0)
|
||||
omniauth-rails_csrf_protection (~> 1.0)
|
||||
omniauth-twitter (~> 1.4)
|
||||
parallel (~> 1.21)
|
||||
|
|
|
|||
3
app/assets/images/google_oauth2.svg
Normal file
3
app/assets/images/google_oauth2.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 640 640">
|
||||
<path d="M326.331 274.255v109.761h181.49c-7.37 47.115-54.886 138.002-181.49 138.002-109.242 0-198.369-90.485-198.369-202.006 0-111.509 89.127-201.995 198.369-201.995 62.127 0 103.761 26.516 127.525 49.359l86.883-83.635C484.99 31.512 412.741-.012 326.378-.012 149.494-.012 6.366 143.116 6.366 320c0 176.884 143.128 320.012 320.012 320.012 184.644 0 307.256-129.876 307.256-312.653 0-21-2.244-36.993-5.008-52.997l-302.248-.13-.047.024z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 681 B |
|
|
@ -24,6 +24,10 @@
|
|||
--brand-email-bg: #24292e;
|
||||
--brand-email-color: #fff;
|
||||
--brand-email-bg-hover: #000;
|
||||
|
||||
--brand-google_oauth2-bg: #1da1f2;
|
||||
--brand-google_oauth2-color: #fff;
|
||||
--brand-google_oauth2-bg-hover: #0096f2;
|
||||
}
|
||||
|
||||
// Basic styling
|
||||
|
|
@ -325,6 +329,17 @@ label[class*='crayons-btn']:focus-within // label for input[type="file"] made to
|
|||
--color-inverted-hover: var(--brand-email-color);
|
||||
}
|
||||
|
||||
.crayons-btn--brand-google_oauth2 {
|
||||
--bg: var(--brand-google_oauth2-bg);
|
||||
--bg-hover: var(--brand-google_oauth2-bg-hover);
|
||||
--color: var(--brand-google_oauth2-color);
|
||||
--color-hover: var(--brand-google_oauth2-color);
|
||||
--bg-inverted: var(--brand-google_oauth2-bg);
|
||||
--bg-inverted-hover: var(--brand-google_oauth2-bg-hover);
|
||||
--color-inverted: var(--brand-google_oauth2-color);
|
||||
--color-inverted-hover: var(--brand-google_oauth2-color);
|
||||
}
|
||||
|
||||
// Icon alone
|
||||
.crayons-btn--icon,
|
||||
.crayons-btn--icon-rounded {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ module AuthenticationHelper
|
|||
end
|
||||
|
||||
def authentication_available_providers
|
||||
Authentication::Providers.available.map do |provider_name|
|
||||
Authentication::Providers.const_get(provider_name.to_s.titleize)
|
||||
end
|
||||
Authentication::Providers.available_providers
|
||||
end
|
||||
|
||||
def authentication_enabled_providers
|
||||
|
|
|
|||
|
|
@ -59,6 +59,14 @@ module Constants
|
|||
description: "The \"Client Secret\" portion of the GitHub Oauth Apps portal",
|
||||
placeholder: ""
|
||||
},
|
||||
google_oauth2_key: {
|
||||
description: "The \"Client ID\" portion of the OAuth 2.0 page on the Google Cloud Platform portal",
|
||||
placeholder: ""
|
||||
},
|
||||
google_oauth2_secret: {
|
||||
description: "The \"Client Secret\" portion of the OAuth 2.0 page on the Google Cloud Platform portal",
|
||||
placeholder: ""
|
||||
},
|
||||
invite_only_mode: {
|
||||
description: "Only users invited by email can join this community.",
|
||||
placeholder: ""
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ module Settings
|
|||
setting :forem_secret, type: :string
|
||||
setting :github_key, type: :string, default: ApplicationConfig["GITHUB_KEY"]
|
||||
setting :github_secret, type: :string, default: ApplicationConfig["GITHUB_SECRET"]
|
||||
setting :google_oauth2_key, type: :string
|
||||
setting :google_oauth2_secret, type: :string
|
||||
setting :invite_only_mode, type: :boolean, default: false
|
||||
setting :providers, type: :array, default: %w[]
|
||||
setting :require_captcha_for_email_password_registration, type: :boolean, default: false
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module Authentication
|
|||
module Providers
|
||||
# Retrieves a provider that is both available and enabled
|
||||
def self.get!(provider_name)
|
||||
name = provider_name.to_s.titleize
|
||||
name = provider_name.to_s.camelize
|
||||
|
||||
unless available?(provider_name)
|
||||
raise(
|
||||
|
|
@ -32,12 +32,16 @@ module Authentication
|
|||
|
||||
def self.available
|
||||
Authentication::Providers::Provider.subclasses.map do |subclass|
|
||||
subclass.name.demodulize.downcase.to_sym
|
||||
subclass.name.demodulize.underscore.downcase.to_sym
|
||||
end.sort
|
||||
end
|
||||
|
||||
def self.available_providers
|
||||
Authentication::Providers::Provider.subclasses.sort_by(&:name)
|
||||
end
|
||||
|
||||
def self.available?(provider_name)
|
||||
Authentication::Providers.const_defined?(provider_name.to_s.titleize)
|
||||
Authentication::Providers.const_defined?(provider_name.to_s.camelize)
|
||||
end
|
||||
|
||||
# Returns enabled providers
|
||||
|
|
|
|||
55
app/services/authentication/providers/google_oauth2.rb
Normal file
55
app/services/authentication/providers/google_oauth2.rb
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
module Authentication
|
||||
module Providers
|
||||
# Google OAuth 2.0 authentication provider, uses omniauth-google-oauth2 as backend
|
||||
class GoogleOauth2 < Provider
|
||||
OFFICIAL_NAME = "Google".freeze
|
||||
SETTINGS_URL = "https://console.cloud.google.com/apis/credentials".freeze
|
||||
|
||||
def new_user_data
|
||||
{
|
||||
name: info.name,
|
||||
email: info.email || "",
|
||||
remote_profile_image_url: Users::SafeRemoteProfileImageUrl.call(@info.image),
|
||||
google_oauth2_username: user_nickname
|
||||
}
|
||||
end
|
||||
|
||||
def existing_user_data
|
||||
{
|
||||
google_oauth2_username: info.name
|
||||
}
|
||||
end
|
||||
|
||||
# We're overriding this method because Google doesn't have a concept nickname or username.
|
||||
# Instead: we'll construct one based on the user's name with some randomization thrown in based
|
||||
# on uid, which is guaranteed to be present and unique from Google.
|
||||
def user_nickname
|
||||
[
|
||||
info.name.sub(" ", "_"),
|
||||
Digest::SHA512.hexdigest(auth_payload.uid),
|
||||
].join("_")[0...25]
|
||||
end
|
||||
|
||||
def self.official_name
|
||||
OFFICIAL_NAME
|
||||
end
|
||||
|
||||
def self.settings_url
|
||||
SETTINGS_URL
|
||||
end
|
||||
|
||||
def self.sign_in_path(**kwargs)
|
||||
::Authentication::Paths.sign_in_path(
|
||||
"google_oauth2",
|
||||
**kwargs,
|
||||
)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def cleanup_payload(auth_payload)
|
||||
auth_payload
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -34,7 +34,7 @@ module Authentication
|
|||
end
|
||||
|
||||
def self.provider_name
|
||||
name.demodulize.downcase.to_sym
|
||||
name.demodulize.underscore.to_sym
|
||||
end
|
||||
|
||||
def self.user_username_field
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ module Metrics
|
|||
welcome_notification_customize_feed
|
||||
welcome_notification_twitter_connect
|
||||
welcome_notification_github_connect
|
||||
welcome_notification_google_connect
|
||||
welcome_notification_customize_experience
|
||||
welcome_notification_discuss_and_ask
|
||||
welcome_notification_download_app
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ GITHUB_OMNIAUTH_SETUP = lambda do |env|
|
|||
env["omniauth.strategy"].options[:scope] = "user:email"
|
||||
end
|
||||
|
||||
GOOGLE_OAUTH2_OMNIAUTH_SETUP = lambda do |env|
|
||||
env["omniauth.strategy"].options[:client_id] = Settings::Authentication.google_oauth2_key
|
||||
env["omniauth.strategy"].options[:client_secret] = Settings::Authentication.google_oauth2_secret
|
||||
end
|
||||
|
||||
FACEBOOK_OMNIAUTH_SETUP = lambda do |env|
|
||||
env["omniauth.strategy"].options[:client_id] = Settings::Authentication.facebook_key
|
||||
env["omniauth.strategy"].options[:client_secret] = Settings::Authentication.facebook_secret
|
||||
|
|
@ -321,6 +326,7 @@ Devise.setup do |config|
|
|||
# Fun fact, unless Twitter is last, it doesn't work for some reason.
|
||||
config.omniauth :facebook, setup: FACEBOOK_OMNIAUTH_SETUP
|
||||
config.omniauth :github, setup: GITHUB_OMNIAUTH_SETUP
|
||||
config.omniauth :google_oauth2, setup: GOOGLE_OAUTH2_OMNIAUTH_SETUP
|
||||
config.omniauth :apple, setup: APPLE_OMNIAUTH_SETUP
|
||||
config.omniauth :forem, setup: FOREM_OMNIAUTH_SETUP, strategy_class: OmniAuth::Strategies::Forem
|
||||
config.omniauth :twitter, setup: TWITTER_OMNIAUTH_SETUP
|
||||
|
|
|
|||
6
db/migrate/20211220170907_add_google_o_auth_fields.rb
Normal file
6
db/migrate/20211220170907_add_google_o_auth_fields.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class AddGoogleOAuthFields < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :users, :google_oauth2_username, :string
|
||||
add_column :users, :google_oauth2_created_at, :datetime
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
class AddGoogleOAuthFieldsIndex < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_index :users, :google_oauth2_username, algorithm: :concurrently
|
||||
end
|
||||
end
|
||||
|
|
@ -1209,6 +1209,8 @@ ActiveRecord::Schema.define(version: 2021_12_22_040359) do
|
|||
t.string "forem_username"
|
||||
t.datetime "github_repos_updated_at", default: "2017-01-01 05:00:00"
|
||||
t.string "github_username"
|
||||
t.datetime "google_oauth2_created_at"
|
||||
t.string "google_oauth2_username"
|
||||
t.datetime "invitation_accepted_at"
|
||||
t.datetime "invitation_created_at"
|
||||
t.integer "invitation_limit"
|
||||
|
|
@ -1270,6 +1272,7 @@ ActiveRecord::Schema.define(version: 2021_12_22_040359) do
|
|||
t.index ["facebook_username"], name: "index_users_on_facebook_username"
|
||||
t.index ["feed_fetched_at"], name: "index_users_on_feed_fetched_at"
|
||||
t.index ["github_username"], name: "index_users_on_github_username", unique: true
|
||||
t.index ["google_oauth2_username"], name: "index_users_on_google_oauth2_username"
|
||||
t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
|
||||
t.index ["invitations_count"], name: "index_users_on_invitations_count"
|
||||
t.index ["invited_by_id"], name: "index_users_on_invited_by_id"
|
||||
|
|
|
|||
|
|
@ -369,6 +369,8 @@ seeder.create_if_none(Broadcast) do
|
|||
"Consider <a href='/settings'>connecting it</a>.",
|
||||
github_connect: "You're on a roll! 🎉 Do you have a GitHub account? " \
|
||||
"Consider <a href='/settings'>connecting it</a> so you can pin any of your repos to your profile.",
|
||||
google_oauth2_connect: "You're on a roll! 🎉 Do you have a Google account? " \
|
||||
"Consider <a href='/settings'>connecting it</a>.",
|
||||
apple_connect: "You're on a roll! 🎉 Do you have an Apple account? " \
|
||||
"Consider <a href='/settings'>connecting it</a>.",
|
||||
customize_feed:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
module DataUpdateScripts
|
||||
class InsertGoogleOuauth2ConnectBroadcastMessage
|
||||
def run
|
||||
return if Broadcast.find_by(title: "Welcome Notification: google_oauth2_connect")
|
||||
|
||||
message = "You're on a roll! 🎉 Do you have a Google account? " \
|
||||
"Consider <a href='/settings'>connecting it</a>."
|
||||
|
||||
Broadcast.create!(
|
||||
title: "Welcome Notification: google_oauth2_connect",
|
||||
processed_html: message,
|
||||
type_of: "Welcome",
|
||||
active: true,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -39,6 +39,15 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
factory :google_oauth2_connect_broadcast do
|
||||
title { "Welcome Notification: google_oauth2_connect" }
|
||||
type_of { "Welcome" }
|
||||
processed_html do
|
||||
"You're on a roll! 🎉 Do you have a Google account? Consider " \
|
||||
"<a href='/settings'>connecting it</a>."
|
||||
end
|
||||
end
|
||||
|
||||
factory :facebook_connect_broadcast do
|
||||
title { "Welcome Notification: facebook_connect" }
|
||||
type_of { "Welcome" }
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ RSpec.describe AuthenticationHelper, type: :helper do
|
|||
allow(Authentication::Providers).to receive(:enabled).and_return(providers)
|
||||
allow(user).to receive(:identities).and_return(user.identities.where(provider: providers))
|
||||
|
||||
expect(helper.signed_up_with(user)).to match(/GitHub and Twitter/)
|
||||
expect(helper.signed_up_with(user)).to match(/Google and Twitter/)
|
||||
expect(helper.signed_up_with(user)).to match(/use any of those/)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_update_scripts/20220107215659_insert_google_ouauth2_connect_broadcast_message.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::InsertGoogleOuauth2ConnectBroadcastMessage do
|
||||
it "works without a Broadcast" do
|
||||
expect do
|
||||
described_class.new.run
|
||||
end.to change(Broadcast, :count).by(1)
|
||||
end
|
||||
|
||||
it "works when a Broadcast already exists" do
|
||||
create(:google_oauth2_connect_broadcast)
|
||||
expect do
|
||||
described_class.new.run
|
||||
end.not_to change(Broadcast, :count)
|
||||
end
|
||||
end
|
||||
|
|
@ -453,7 +453,7 @@ RSpec.describe User, type: :model do
|
|||
case provider_name
|
||||
when :apple
|
||||
expect(new_user.username).to match(/valid_username_\w+/)
|
||||
when :facebook
|
||||
when :facebook, :google_oauth2
|
||||
expect(new_user.username).to match(/fname_lname_\S*\z/)
|
||||
else
|
||||
expect(new_user.username).to eq("valid_username")
|
||||
|
|
@ -472,7 +472,7 @@ RSpec.describe User, type: :model do
|
|||
case provider_name
|
||||
when :apple
|
||||
expect(new_user.username).to match(/invalidusername_\w+/)
|
||||
when :facebook
|
||||
when :facebook, :google_oauth2
|
||||
expect(new_user.username).to match(/fname_lname_\S*\z/)
|
||||
else
|
||||
expect(new_user.username).to eq("invalidusername")
|
||||
|
|
|
|||
|
|
@ -24,15 +24,35 @@ RSpec.describe Authentication::Providers, type: :service do
|
|||
)
|
||||
expect(is_subclass_of).to be(true)
|
||||
end
|
||||
|
||||
it "handles spaces in provider names" do
|
||||
allow(Settings::Authentication).to receive(:providers).and_return(%w[google_oauth2])
|
||||
|
||||
expect do
|
||||
described_class.get!(:google_oauth2)
|
||||
end.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe ".available" do
|
||||
it "lists the available providers" do
|
||||
available_providers = %i[apple facebook forem github twitter]
|
||||
available_providers = %i[apple facebook forem github google_oauth2 twitter]
|
||||
expect(described_class.available).to eq(available_providers)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".availble_providers" do
|
||||
it "lists the available providers" do
|
||||
available_providers = [Authentication::Providers::Forem,
|
||||
Authentication::Providers::Facebook,
|
||||
Authentication::Providers::Apple,
|
||||
Authentication::Providers::Twitter,
|
||||
Authentication::Providers::GoogleOauth2,
|
||||
Authentication::Providers::Github].sort_by(&:name)
|
||||
expect(described_class.available_providers).to eq(available_providers)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".enabled" do
|
||||
context "when one of the available providers is disabled" do
|
||||
it "only lists those that remain enabled" do
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ RSpec.describe Broadcasts::WelcomeNotification::Generator, type: :service do
|
|||
let!(:facebook_connect_broadcast) { create(:facebook_connect_broadcast) }
|
||||
let!(:apple_connect_broadcast) { create(:apple_connect_broadcast) }
|
||||
let!(:forem_connect_broadcast) { create(:forem_connect_broadcast) }
|
||||
let!(:google_oauth2_connect_broadcast) { create(:google_oauth2_connect_broadcast) }
|
||||
let!(:customize_feed_broadcast) { create(:customize_feed_broadcast) }
|
||||
let!(:discuss_and_ask_broadcast) { create(:discuss_and_ask_broadcast) }
|
||||
let!(:customize_ux_broadcast) { create(:customize_ux_broadcast) }
|
||||
|
|
@ -70,6 +71,7 @@ RSpec.describe Broadcasts::WelcomeNotification::Generator, type: :service do
|
|||
twitter_connect_broadcast,
|
||||
apple_connect_broadcast,
|
||||
forem_connect_broadcast,
|
||||
google_oauth2_connect_broadcast,
|
||||
].include?(user.notifications.last.notifiable)
|
||||
expect(not_github).to be(true)
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,55 @@ module OmniauthHelpers
|
|||
},
|
||||
).freeze
|
||||
|
||||
OMNIAUTH_PAYLOAD_GOOGLE_OAUTH2 = OmniAuth::AuthHash::InfoHash.new(
|
||||
{
|
||||
"provider" => "google_oauth2",
|
||||
"uid" => "100000000000000000000",
|
||||
"info" => {
|
||||
"name" => "fname lname",
|
||||
"email" => "john@example.com",
|
||||
"first_name" => "fname",
|
||||
"last_name" => "lname",
|
||||
"image" => "https://dummyimage.com/400x400.jpg",
|
||||
"urls" => {
|
||||
"google" => "https://plus.google.com/+JohnSmith"
|
||||
}
|
||||
},
|
||||
"credentials" => {
|
||||
"token" => "TOKEN",
|
||||
"refresh_token" => "REFRESH_TOKEN",
|
||||
"expires_at" => 1_496_120_719,
|
||||
"expires" => true
|
||||
},
|
||||
"extra" => {
|
||||
"id_token" => "ID_TOKEN",
|
||||
"id_info" => {
|
||||
"azp" => "APP_ID",
|
||||
"aud" => "APP_ID",
|
||||
"sub" => "100000000000000000000",
|
||||
"email" => "john@example.com",
|
||||
"email_verified" => true,
|
||||
"at_hash" => "HK6E_P6Dh8Y93mRNtsDB1Q",
|
||||
"iss" => "accounts.google.com",
|
||||
"iat" => 1_496_117_119,
|
||||
"exp" => 1_496_120_719
|
||||
},
|
||||
"raw_info" => {
|
||||
"sub" => "100000000000000000000",
|
||||
"name" => "fname lname",
|
||||
"given_name" => "fname",
|
||||
"family_name" => "lname",
|
||||
"profile" => "https://plus.google.com/+JohnSmith",
|
||||
"picture" => "https://dummyimage.com/400x400.jpg",
|
||||
"email" => "john@example.com",
|
||||
"email_verified" => "true",
|
||||
"locale" => "en",
|
||||
"hd" => "company.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
).freeze
|
||||
|
||||
def omniauth_setup_invalid_credentials(provider)
|
||||
OmniAuth.config.mock_auth[provider] = :invalid_credentials
|
||||
end
|
||||
|
|
@ -198,6 +247,10 @@ module OmniauthHelpers
|
|||
OmniAuth.config.mock_auth[:apple] = OMNIAUTH_PAYLOAD_APPLE.dup
|
||||
end
|
||||
|
||||
def omniauth_mock_google_oauth2_payload
|
||||
OmniAuth.config.mock_auth[:google_oauth2] = OMNIAUTH_PAYLOAD_GOOGLE_OAUTH2.dup
|
||||
end
|
||||
|
||||
def omniauth_mock_github_payload
|
||||
info = OMNIAUTH_BASIC_INFO[:info].merge(
|
||||
image: "https://dummyimage.com/400x400.jpg",
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ RSpec.describe "Creator config edit", type: :system, js: true do
|
|||
click_on("Show info", match: :first)
|
||||
end
|
||||
|
||||
Authentication::Providers.available.each do |provider|
|
||||
element = find(".config-authentication__item--label", text: /#{provider}/i)
|
||||
Authentication::Providers.available_providers.each do |provider|
|
||||
element = find(".config-authentication__item--label", text: /#{provider.official_name}/i)
|
||||
expect(element).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
|
|
|||
BIN
vendor/cache/omniauth-google-oauth2-1.0.0.gem
vendored
Normal file
BIN
vendor/cache/omniauth-google-oauth2-1.0.0.gem
vendored
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue