From 99e525c9d170b4d81a7aab9c49e734142d2b4c83 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 22 Aug 2016 19:51:12 +0300 Subject: [PATCH 01/37] WIP Facebook OAuth --- Gemfile | 3 +++ Gemfile.lock | 30 ++++++++++++++++++++++++++++++ app/models/user.rb | 3 ++- config/initializers/devise.rb | 2 ++ config/secrets.yml | 2 ++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index d96735c..8d1732a 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,9 @@ gem 'private_pub' gem 'skim' gem 'gon' gem 'responders' +gem 'omniauth' +gem 'omniauth-facebook' +gem 'omniauth-twitter' # gem 'unicorn' gem 'thin' diff --git a/Gemfile.lock b/Gemfile.lock index 5730254..075bb70 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -111,6 +111,8 @@ GEM railties (>= 3.0.0) faker (1.6.6) i18n (~> 0.5) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) faye (1.2.2) cookiejar (>= 0.3.0) em-http-request (>= 0.3.0) @@ -146,6 +148,7 @@ GEM guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) + hashie (3.4.4) http_parser.rb (0.6.0) i18n (0.7.0) jbuilder (2.6.0) @@ -156,6 +159,7 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) + jwt (1.5.4) launchy (2.4.3) addressable (~> 2.3) listen (3.1.5) @@ -179,6 +183,8 @@ GEM mini_portile2 (2.1.0) minitest (5.9.0) multi_json (1.12.1) + multi_xml (0.5.5) + multipart-post (2.0.0) nenv (0.3.0) nokogiri (1.6.8) mini_portile2 (~> 2.1.0) @@ -186,6 +192,27 @@ GEM notiffany (0.1.0) nenv (~> 0.1) shellany (~> 0.0) + oauth (0.5.1) + oauth2 (1.2.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.3.1) + hashie (>= 1.2, < 4) + rack (>= 1.0, < 3) + omniauth-facebook (4.0.0) + omniauth-oauth2 (~> 1.2) + omniauth-oauth (1.1.0) + oauth + omniauth (~> 1.0) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) + omniauth (~> 1.2) + omniauth-twitter (1.2.1) + json (~> 1.3) + omniauth-oauth (~> 1.1) orm_adapter (0.5.0) parser (2.3.1.2) ast (~> 2.2) @@ -373,6 +400,9 @@ DEPENDENCIES jquery-rails launchy meta_request + omniauth + omniauth-facebook + omniauth-twitter pg (~> 0.15) private_pub pry diff --git a/app/models/user.rb b/app/models/user.rb index 3aab7c2..5875ba9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,7 +2,8 @@ class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :trackable, :validatable + :recoverable, :rememberable, :trackable, :validatable, + :omniauthable, omniauth_providers: [:facebook, :twitter] has_many :questions, dependent: :destroy has_many :answers, dependent: :destroy diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 09f665c..c0d7081 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -242,6 +242,8 @@ # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' + config.omniauth :facebook, Rails.application.secrets.facebook_app_id, + Rails.application.secrets.facebook_app_secret, scope: [:email] # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or diff --git a/config/secrets.yml b/config/secrets.yml index ff4b40e..1147f10 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -12,6 +12,8 @@ development: secret_key_base: b95a307aeef7b76a6805c4f196f9f228bd8b7e40d78dcae5b003e55b851b299189b273e11148c542a8a5e06b44c1b97fcabb2922a1a02e515e498998a228766f + facebook_app_id: 1111783555576872 + facebook_app_secret: cc941c1c69c4df0cd160e2bca79fce00 test: secret_key_base: 409050a6e334ef505b4403e78afbe40e18007505904d27227ce6302b311a1b2084c3f0028f4a06fa86a718e7e4846e492a20d26f768c7c9672828e9c5fef391b From 480d66e7d7903ea1cc2d1377015dc0fe13eedfa2 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 23 Aug 2016 03:25:23 +0300 Subject: [PATCH 02/37] User model spec, User.find_for_oauth method --- .../omniauth_callbacks_controller.rb | 13 ++++ app/models/identity.rb | 3 + app/models/user.rb | 21 +++++- config/routes.rb | 2 +- .../20160822233027_create_identities.rb | 13 ++++ db/schema.rb | 14 +++- spec/factories/identities.rb | 7 ++ spec/models/identity_spec.rb | 5 ++ spec/models/user_spec.rb | 70 +++++++++++++++++++ 9 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 app/controllers/omniauth_callbacks_controller.rb create mode 100644 app/models/identity.rb create mode 100644 db/migrate/20160822233027_create_identities.rb create mode 100644 spec/factories/identities.rb create mode 100644 spec/models/identity_spec.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb new file mode 100644 index 0000000..660a63c --- /dev/null +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -0,0 +1,13 @@ +class OmniauthCallbacksController < Devise::OmniauthCallbacksController + def facebook + @user = User.find_for_oauth(request.env['omniauth.auth']) + if @user.persisted? + sign_in_and_redirect @user, event: :authentication + set_flash_message :notice, :success, kind: 'Facebook' if is_navigational_format? + end + end + + # def twitter + # #TODO: twitter oauth + # end +end \ No newline at end of file diff --git a/app/models/identity.rb b/app/models/identity.rb new file mode 100644 index 0000000..ad16bc1 --- /dev/null +++ b/app/models/identity.rb @@ -0,0 +1,3 @@ +class Identity < ActiveRecord::Base + belongs_to :user +end diff --git a/app/models/user.rb b/app/models/user.rb index 5875ba9..00643d6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,10 +2,27 @@ class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :trackable, :validatable, - :omniauthable, omniauth_providers: [:facebook, :twitter] + :recoverable, :rememberable, :trackable, :validatable, + :omniauthable, omniauth_providers: [:facebook, :twitter] + has_many :identities, dependent: :destroy has_many :questions, dependent: :destroy has_many :answers, dependent: :destroy has_many :comments, dependent: :destroy + + class << self + def find_for_oauth(auth) + identity = Identity.where(provider: auth.provider, uid: auth.uid.to_s).first + return identity.user if identity + + email = auth.info[:email] + user = User.where(email: email).first + unless user + password = Devise.friendly_token(20) + user = User.create!(email: email, password: password, password_confirmation: password) + end + user.identities.create(provider: auth.provider, uid: auth.uid) if user + user + end + end end diff --git a/config/routes.rb b/config/routes.rb index ba20d7a..81fca92 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ Rails.application.routes.draw do root 'questions#index' - devise_for :users + devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks} concern :rateable do member do diff --git a/db/migrate/20160822233027_create_identities.rb b/db/migrate/20160822233027_create_identities.rb new file mode 100644 index 0000000..2634a1a --- /dev/null +++ b/db/migrate/20160822233027_create_identities.rb @@ -0,0 +1,13 @@ +class CreateIdentities < ActiveRecord::Migration + def change + create_table :identities do |t| + t.references :user, index: true, foreign_key: true + t.string :provider, null: false + t.string :uid, null: false + + t.timestamps null: false + end + + add_index :identities, [:provider, :uid] + end +end diff --git a/db/schema.rb b/db/schema.rb index 42a97fd..489ac33 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160806125740) do +ActiveRecord::Schema.define(version: 20160822233027) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -48,6 +48,17 @@ t.datetime "updated_at", null: false end + create_table "identities", force: :cascade do |t| + t.integer "user_id" + t.string "provider", null: false + t.string "uid", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "identities", ["provider", "uid"], name: "index_identities_on_provider_and_uid", using: :btree + add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree + create_table "questions", force: :cascade do |t| t.string "topic", limit: 200, null: false t.text "body", null: false @@ -92,6 +103,7 @@ add_foreign_key "answers", "questions" add_foreign_key "answers", "users" add_foreign_key "comments", "users" + add_foreign_key "identities", "users" add_foreign_key "questions", "users" add_foreign_key "ratings", "users" end diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb new file mode 100644 index 0000000..c5c3012 --- /dev/null +++ b/spec/factories/identities.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :identity do + user nil + provider "MyString" + uid "MyString" + end +end diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb new file mode 100644 index 0000000..53f3554 --- /dev/null +++ b/spec/models/identity_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Identity, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 25718f3..9430e8f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,9 +1,79 @@ require 'rails_helper' RSpec.describe User do + it { is_expected.to have_many(:identities).dependent(:destroy) } it { is_expected.to have_many(:questions).dependent(:destroy) } it { is_expected.to have_many(:answers).dependent(:destroy) } + it { is_expected.to have_many(:comments).dependent(:destroy) } it { is_expected.to validate_presence_of :email } it { is_expected.to validate_presence_of :password } + + describe '.find_for_oauth' do + let!(:user) { create(:user) } + let(:auth) { OmniAuth::AuthHash.new(provider: 'facebook', uid: '42') } + + context 'User\'s Identity already exists' do + it 'returns valid user' do + user.identities.create(provider: 'facebook', uid: '42') + expect(User.find_for_oauth(auth)).to eq user + end + end + + context 'User\'s Identity doesn\'t exists' do + context 'User elready exists' do + let(:auth) { OmniAuth::AuthHash.new(provider: 'facebook', uid: '42', + info: {email: user.email}) } + + it 'doesn\'t create new User' do + expect { User.find_for_oauth(auth) }.to_not change(User, :count) + end + + it 'creates Identity for the User' do + expect { User.find_for_oauth(auth) }.to change(user.identities, :count).by(1) + end + + it 'creates Identity with provider and uid' do + identity = User.find_for_oauth(auth).identities.first + + expect(identity.provider).to eq auth.provider + expect(identity.uid).to eq auth.uid + end + + it 'ruturns User object' do + expect(User.find_for_oauth(auth)).to eq user + end + end + + context 'User doesn\'t exists' do + let(:auth) { OmniAuth::AuthHash.new(provider: 'facebook', uid: '42', + info: {email: 'notexistent@user.com'}) } + + it 'creates new User' do + expect { User.find_for_oauth(auth) }.to change(User, :count).by(1) + end + + it 'returns new User' do + expect(User.find_for_oauth(auth)).to be_a(User) + end + + it 'fills User\'s email' do + user = User.find_for_oauth(auth) + expect(user.email).to eq auth.info.email + end + + it 'creates Identity for the User' do + user = User.find_for_oauth(auth) + expect(user.identities).to_not be_empty + end + + it 'creates Identity with provider and uid' do + identity = User.find_for_oauth(auth).identities.first + + expect(identity.provider).to eq auth.provider + expect(identity.uid).to eq auth.uid + end + end + end + end end From f85e9ae4f70d668331ef7e49ccddbd18d26e5e8c Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 23 Aug 2016 16:27:41 +0300 Subject: [PATCH 03/37] Add Twitter in callbacks controller --- app/controllers/omniauth_callbacks_controller.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 660a63c..eb20de1 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -7,7 +7,11 @@ def facebook end end - # def twitter - # #TODO: twitter oauth - # end -end \ No newline at end of file + def twitter + @user = User.find_for_oauth(request.env['omniauth.auth']) + if @user.persisted? + sign_in_and_redirect @user, event: :authentication + set_flash_message :notice, :success, kind: 'Twitter' if is_navigational_format? + end + end +end From cfc6b6d30798d4151ace61f89dd00ede3732049c Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 23 Aug 2016 16:28:23 +0300 Subject: [PATCH 04/37] Add Identity model validations and specs --- app/models/identity.rb | 3 +++ spec/factories/identities.rb | 16 ++++++++++++++-- spec/models/identity_spec.rb | 13 +++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/models/identity.rb b/app/models/identity.rb index ad16bc1..efce3a8 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,3 +1,6 @@ class Identity < ActiveRecord::Base belongs_to :user + + validates :user_id, :uid, :provider, presence: true + validates :uid, uniqueness: {scope: :provider} end diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb index c5c3012..c5fa824 100644 --- a/spec/factories/identities.rb +++ b/spec/factories/identities.rb @@ -1,7 +1,19 @@ FactoryGirl.define do factory :identity do + user + provider 'deeprec' + uid 'eehieWijeshingaet8ch' + end + + factory :alt_identity, class: 'Identity' do + user + provider 'theotherprovider' + uid 'dengahpo4tae8Iew1eem' + end + + factory :invalid_identity, class: 'Identity' do user nil - provider "MyString" - uid "MyString" + provider nil + uid nil end end diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb index 53f3554..3c54054 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -1,5 +1,14 @@ require 'rails_helper' -RSpec.describe Identity, type: :model do - pending "add some examples to (or delete) #{__FILE__}" +describe Identity do + it { is_expected.to belong_to :user } + + it { is_expected.to validate_presence_of :user_id } + it { is_expected.to validate_presence_of :uid } + it { is_expected.to validate_presence_of :provider } + + describe 'should validate uniqueness of uid for the provider' do + subject { create(:alt_identity) } + it { is_expected.to validate_uniqueness_of(:uid).scoped_to(:provider) } + end end From d7fbdce5c6731b2d2a5f681202f5b0d40a4f6ca7 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 25 Aug 2016 13:16:49 +0300 Subject: [PATCH 05/37] bundle update to avoid deprecation warnings --- Gemfile.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 075bb70..60caf0e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,7 +39,7 @@ GEM addressable (2.4.0) arel (6.0.3) ast (2.3.0) - autoprefixer-rails (6.3.7) + autoprefixer-rails (6.4.0.3) execjs bcrypt (3.1.11) better_errors (2.1.1) @@ -71,7 +71,7 @@ GEM mime-types (>= 1.16) mimemagic (>= 0.3.0) cocoon (1.2.9) - code_analyzer (0.4.5) + code_analyzer (0.4.7) sexp_processor coderay (1.1.1) coffee-rails (4.1.1) @@ -83,7 +83,7 @@ GEM coffee-script-source (1.10.0) concurrent-ruby (1.0.2) cookiejar (0.3.3) - daemons (1.2.3) + daemons (1.2.4) database_cleaner (1.5.3) debug_inspector (0.0.2) devise (4.2.0) @@ -144,7 +144,7 @@ GEM shellany (~> 0.0) thor (>= 0.18.1) guard-compat (1.2.1) - guard-rspec (4.7.2) + guard-rspec (4.7.3) guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) @@ -154,7 +154,7 @@ GEM jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) - jquery-rails (4.1.1) + jquery-rails (4.2.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) @@ -179,7 +179,7 @@ GEM mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) - mimemagic (0.3.1) + mimemagic (0.3.2) mini_portile2 (2.1.0) minitest (5.9.0) multi_json (1.12.1) @@ -189,7 +189,7 @@ GEM nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) - notiffany (0.1.0) + notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) oauth (0.5.1) @@ -280,13 +280,13 @@ GEM remotipart (1.2.1) request_store (1.3.1) require_all (1.3.3) - responders (2.2.0) + responders (2.3.0) railties (>= 4.2.0, < 5.1) rspec (3.5.0) rspec-core (~> 3.5.0) rspec-expectations (~> 3.5.0) rspec-mocks (~> 3.5.0) - rspec-core (3.5.1) + rspec-core (3.5.2) rspec-support (~> 3.5.0) rspec-expectations (3.5.0) diff-lcs (>= 1.2.0, < 2.0) @@ -310,7 +310,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) - ruby_dep (1.3.1) + ruby_dep (1.4.0) sass (3.4.22) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -356,12 +356,12 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (2.0.5) - turbolinks (5.0.0) + turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (3.0.0) + uglifier (3.0.2) execjs (>= 0.3.0, < 3) unicode-display_width (1.1.0) warden (1.2.6) From 5df4c6e6e51d501d23a74806f6edfa70f04131b1 Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 25 Aug 2016 15:38:19 +0300 Subject: [PATCH 06/37] WIP Feature spec for OAuth login --- spec/features/sign_in_oauth.rb | 40 +++++++++++++++++++++++++++++++++ spec/features_helper.rb | 4 ++++ spec/support/omniauth_macros.rb | 18 +++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 spec/features/sign_in_oauth.rb create mode 100644 spec/support/omniauth_macros.rb diff --git a/spec/features/sign_in_oauth.rb b/spec/features/sign_in_oauth.rb new file mode 100644 index 0000000..ba915f0 --- /dev/null +++ b/spec/features/sign_in_oauth.rb @@ -0,0 +1,40 @@ +require 'features_helper' + + +shared_examples :oauth_sign_in do + given(:user) { create(:user) } + + context 'with email' do + scenario 'do new registraion' do + provider_name = provider.to_s.capitalize + link_text = "Sign in with #{provider_name}" + visit new_user_session_path + expect(page).to have_link link_text + mock_auth_hash(provider, {info: { email: user.email}}) + click_link link_text + auth_success_text = "GLYPH:info-sign Successfully authenticated from #{provider_name} account." + expect(page).to have_text auth_success_text + end + end +end + +feature 'Signing in using OAuth', %q( + To ask questions + As a user + I want to sign in using Facebook or Twitter +) do + + context 'Logging in with Facebook OAuth' do + given!(:provider) { :facebook } + given!(:identity) { create(:identity, provider: provider) } + + it_behaves_like :oauth_sign_in + end + + # context 'Logging in with Twitter OAuth' do + # given(:provider) { :twitter } + # given!(:identity) { create(:identity, provider: provider) } + # + # it_behaves_like :oauth_sign_in + # end +end diff --git a/spec/features_helper.rb b/spec/features_helper.rb index 1302751..d920dca 100644 --- a/spec/features_helper.rb +++ b/spec/features_helper.rb @@ -2,6 +2,8 @@ require 'tilt/coffee' RSpec.configure do |config| + config.include(OmniauthMacros) + Capybara.javascript_driver = :webkit Capybara.default_max_wait_time = 10 @@ -33,3 +35,5 @@ DatabaseCleaner.clean end end + +OmniAuth.config.test_mode = true diff --git a/spec/support/omniauth_macros.rb b/spec/support/omniauth_macros.rb new file mode 100644 index 0000000..809f8de --- /dev/null +++ b/spec/support/omniauth_macros.rb @@ -0,0 +1,18 @@ +module OmniauthMacros + def mock_auth_hash(provider, oauth_params = {}) + OmniAuth.config.mock_auth[provider] = OmniAuth::AuthHash.new( + { + provider: provider, + uid: '123545', + info: {email: 'someuser@somehost.dom'} + # 'user_info' => { + # 'name' => 'mockuser', + # 'image' => 'mock_user_thumbnail_url' + # }, + # 'credentials' => { + # 'token' => 'mock_token', + # 'secret' => 'mock_secret' + # } + }.merge oauth_params) + end +end From e188fc1da7a711f1eb65805be842f56529ba5d77 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Mon, 29 Aug 2016 01:30:50 +0300 Subject: [PATCH 07/37] Fix typo in spec --- spec/models/user_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 9430e8f..4da733a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -40,7 +40,7 @@ expect(identity.uid).to eq auth.uid end - it 'ruturns User object' do + it 'returns User object' do expect(User.find_for_oauth(auth)).to eq user end end From 1b33465ec16b8f178c8f66e2d6a9925ab3ce9165 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Mon, 29 Aug 2016 01:31:52 +0300 Subject: [PATCH 08/37] Add circle.yml to fix broken defaults of CircleCI --- circle.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..62eac23 --- /dev/null +++ b/circle.yml @@ -0,0 +1,5 @@ +dependencies: + pre: + - gem install bundler --pre + post: + - bin/comet & From 25e7ac650d2d1a31e96dbb0d83531ea2dd92a2dd Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Mon, 29 Aug 2016 03:04:55 +0300 Subject: [PATCH 09/37] Switch Ruby Version to 2.3.1 explicitly --- .ruby-version | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ruby-version b/.ruby-version index 2078687..c1026d2 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-2.3-head +ruby-2.3.1 diff --git a/Gemfile.lock b/Gemfile.lock index 60caf0e..43d0070 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -294,7 +294,7 @@ GEM rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.5.0) - rspec-rails (3.5.1) + rspec-rails (3.5.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) From b77f6f6a262196ee6d2bc79b33722f3b9e483cf5 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Mon, 29 Aug 2016 03:10:29 +0300 Subject: [PATCH 10/37] Try to downgrade Ruby to 2.3.0 for CircleCI only --- circle.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/circle.yml b/circle.yml index 62eac23..3688c77 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,9 @@ +machine: + timezone: + Europe/Moscow + ruby: + version: 2.3.0 + dependencies: pre: - gem install bundler --pre From 1033b6f4e38e9ccc7656dce8feeaee616c4568c5 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Mon, 29 Aug 2016 03:15:27 +0300 Subject: [PATCH 11/37] Daemonize rackup in bin/comet --- bin/comet | 2 +- circle.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/comet b/bin/comet index e600183..4053693 100755 --- a/bin/comet +++ b/bin/comet @@ -1,5 +1,5 @@ #!/bin/sh #rackup private_pub.ru -s thin -E development -rackup private_pub.ru -s thin -E production +rackup -D private_pub.ru -s thin -E production diff --git a/circle.yml b/circle.yml index 3688c77..97b251b 100644 --- a/circle.yml +++ b/circle.yml @@ -8,4 +8,4 @@ dependencies: pre: - gem install bundler --pre post: - - bin/comet & + - bin/comet From b44f65a9efa63c71014e827dbcd996975c61ae2e Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 29 Aug 2016 12:47:58 +0300 Subject: [PATCH 12/37] One more fix ix circle.yml --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 97b251b..46f7575 100644 --- a/circle.yml +++ b/circle.yml @@ -8,4 +8,4 @@ dependencies: pre: - gem install bundler --pre post: - - bin/comet + - bundle exec ./bin/comet From f30714774c7e20a0510cdfa063077278858dd1c2 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Mon, 29 Aug 2016 22:01:35 +0300 Subject: [PATCH 13/37] Add :confirmable to Users (reconfigure Devise) --- app/models/user.rb | 1 + config/initializers/devise.rb | 6 +++--- ...20160829190531_add_confirmable_to_devise.rb | 18 ++++++++++++++++++ db/schema.rb | 7 ++++++- 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20160829190531_add_confirmable_to_devise.rb diff --git a/app/models/user.rb b/app/models/user.rb index 00643d6..3485ae2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,6 +3,7 @@ class User < ActiveRecord::Base # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, + :confirmable, :omniauthable, omniauth_providers: [:facebook, :twitter] has_many :identities, dependent: :destroy diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index c0d7081..05f8a79 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -12,13 +12,13 @@ # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class # with default "from" parameter. - config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' + config.mailer_sender = 'gene+deeprecursion@deep.msk.ru' # Configure the class responsible to send e-mails. - # config.mailer = 'Devise::Mailer' + config.mailer = 'Devise::Mailer' # Configure the parent class responsible to send e-mails. - # config.parent_mailer = 'ActionMailer::Base' + config.parent_mailer = 'ActionMailer::Base' # ==> ORM configuration # Load and configure the ORM. Supports :active_record (default) and diff --git a/db/migrate/20160829190531_add_confirmable_to_devise.rb b/db/migrate/20160829190531_add_confirmable_to_devise.rb new file mode 100644 index 0000000..f94d2a6 --- /dev/null +++ b/db/migrate/20160829190531_add_confirmable_to_devise.rb @@ -0,0 +1,18 @@ +class AddConfirmableToDevise < ActiveRecord::Migration + def up + add_column :users, :confirmation_token, :string + add_column :users, :confirmed_at, :datetime + add_column :users, :confirmation_sent_at, :datetime + add_column :users, :unconfirmed_email, :string + + add_index :users, :confirmation_token, unique: true + + User.all.update_all confirmed_at: Time.now + end + + def down + remove_index :users, :confirmation_token + + remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at, :unconfirmed_email + end +end diff --git a/db/schema.rb b/db/schema.rb index 489ac33..de4641d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160822233027) do +ActiveRecord::Schema.define(version: 20160829190531) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -95,8 +95,13 @@ t.datetime "last_sign_in_at" t.inet "current_sign_in_ip" t.inet "last_sign_in_ip" + t.string "confirmation_token" + t.datetime "confirmed_at" + t.datetime "confirmation_sent_at" + t.string "unconfirmed_email" end + add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree From 1d1e9d10a39e9d11532267780125ffe3864b4e68 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Mon, 29 Aug 2016 22:54:39 +0300 Subject: [PATCH 14/37] Add and configure 'letter_opener' and 'capybara-email' gems --- Gemfile | 3 +++ Gemfile.lock | 7 +++++++ config/environments/development.rb | 3 ++- spec/features_helper.rb | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 8d1732a..ba874ff 100644 --- a/Gemfile +++ b/Gemfile @@ -44,6 +44,7 @@ group :test do gem 'launchy' gem 'database_cleaner' gem 'capybara-webkit' + gem 'capybara-email' end group :development do @@ -60,4 +61,6 @@ group :development do gem 'rails_best_practices' gem 'spring' + + gem 'letter_opener' end diff --git a/Gemfile.lock b/Gemfile.lock index 43d0070..d32dfde 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,6 +61,9 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + capybara-email (2.5.0) + capybara (~> 2.4) + mail capybara-webkit (1.11.1) capybara (>= 2.3.0, < 2.8.0) json @@ -162,6 +165,8 @@ GEM jwt (1.5.4) launchy (2.4.3) addressable (~> 2.3) + letter_opener (1.4.1) + launchy (~> 2.2) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -386,6 +391,7 @@ DEPENDENCIES bootstrap-sass (~> 3.3.6) byebug (~> 8.0) capybara + capybara-email capybara-webkit carrierwave cocoon @@ -399,6 +405,7 @@ DEPENDENCIES jbuilder (~> 2.0) jquery-rails launchy + letter_opener meta_request omniauth omniauth-facebook diff --git a/config/environments/development.rb b/config/environments/development.rb index b2a1347..c469ec5 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -15,7 +15,8 @@ config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors = true - config.action_mailer.delivery_method = :sendmail + # config.action_mailer.delivery_method = :sendmail + config.action_mailer.delivery_method = :letter_opener config.action_mailer.default_options = { from: 'e@varnakov.ru' } # Print deprecation notices to the Rails logger. diff --git a/spec/features_helper.rb b/spec/features_helper.rb index d920dca..1a7e348 100644 --- a/spec/features_helper.rb +++ b/spec/features_helper.rb @@ -1,5 +1,6 @@ require 'rails_helper' require 'tilt/coffee' +require 'capybara/email/rspec' RSpec.configure do |config| config.include(OmniauthMacros) From 958a16e6f69d4fe0528675cb9fda7d4f5ab3dc69 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 31 Aug 2016 18:17:11 +0300 Subject: [PATCH 15/37] Add Twitter API keys --- config/secrets.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/secrets.yml b/config/secrets.yml index 1147f10..bae4c49 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -14,6 +14,8 @@ development: secret_key_base: b95a307aeef7b76a6805c4f196f9f228bd8b7e40d78dcae5b003e55b851b299189b273e11148c542a8a5e06b44c1b97fcabb2922a1a02e515e498998a228766f facebook_app_id: 1111783555576872 facebook_app_secret: cc941c1c69c4df0cd160e2bca79fce00 + twitter_app_id: 1BeipKxAypv1y4DDbzLN30kja + twitter_app_secret: tAJ0d57JXZpyITrYy8f7kUvlFpcghzav4KwZBthTv2WfKX7IUH test: secret_key_base: 409050a6e334ef505b4403e78afbe40e18007505904d27227ce6302b311a1b2084c3f0028f4a06fa86a718e7e4846e492a20d26f768c7c9672828e9c5fef391b From e3abd400cb42eedb21b484c61e074dd35226c208 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Sat, 3 Sep 2016 01:39:32 +0300 Subject: [PATCH 16/37] Create stub spec for Account controller for email confirmation --- spec/controllers/account_controller_spec.rb | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 spec/controllers/account_controller_spec.rb diff --git a/spec/controllers/account_controller_spec.rb b/spec/controllers/account_controller_spec.rb new file mode 100644 index 0000000..3f51683 --- /dev/null +++ b/spec/controllers/account_controller_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +describe AccountController do + let (:user) { create(:user) } + + describe 'GET #confirm_email' do + context 'as unauthenticated user' do + context 'with valid OAuth data in session' + context 'with invalid OAuth data in session' + context 'with no OAuth data in session' + end + context 'as authenticated user' + end + + describe 'PATCH #confirm_email' do + context 'as unauthenticated user' do + context 'with valid OAuth data in session' + context 'with invalid OAuth data in session' + context 'with no OAuth data in session' + end + context 'as authenticated user' + end +end From 03e5db739c187bf9f4b3e0ae9d16c14983516392 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Sat, 3 Sep 2016 01:40:39 +0300 Subject: [PATCH 17/37] Add routes to confirm email using Account controller --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index 81fca92..0b311d3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,7 @@ root 'questions#index' devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks} + match 'account/confirm_email', via: [:get, :patch] concern :rateable do member do From f65c9788ec3cfef7125757c0d8b69bcd75a9c7c7 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Sat, 3 Sep 2016 02:06:59 +0300 Subject: [PATCH 18/37] Downgrade sprockets gem to 3.6.3 to avoid pesky deprication warning issued by skim gem --- Gemfile | 1 + Gemfile.lock | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ba874ff..5c85e11 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source 'https://rubygems.org' gem 'rails', '4.2.6' gem 'pg', '~> 0.15' +gem 'sprockets', '3.6.3' gem 'sass-rails', '~> 5.0' gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index d32dfde..81b80b3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -346,7 +346,7 @@ GEM spring (1.7.2) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - sprockets (3.7.0) + sprockets (3.6.3) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.1.1) @@ -430,6 +430,7 @@ DEPENDENCIES slim-rails spring spring-commands-rspec + sprockets (= 3.6.3) thin turbolinks uglifier (>= 1.3.0) From ea1c0c0ed03b986fa0951f9364a307f84ee00393 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Sat, 3 Sep 2016 02:39:18 +0300 Subject: [PATCH 19/37] Email confirmation template in draft --- app/views/account/confirm_email.slim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/views/account/confirm_email.slim diff --git a/app/views/account/confirm_email.slim b/app/views/account/confirm_email.slim new file mode 100644 index 0000000..5366133 --- /dev/null +++ b/app/views/account/confirm_email.slim @@ -0,0 +1,14 @@ +h2 Enter your email to proceed + +.row + .col-sm-12#email-errors + +.row + .col-sm-12 + = form_for :user, url: account_confirm_email_path, method: :patch do |f| + .form-group + = f.label :email + = f.email_field :email, class: 'form-control', placeholder: 'enter@email.here' + /= email_field_tag :email, params[:email], class: 'form-control', placeholder: 'enter@email.here' + .form-group + = f.submit 'Validate Email', class: 'btn btn-primary pull-right' \ No newline at end of file From d01a3c0f02efcfed65b1befe7428b48c7b54d8f1 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 00:36:35 +0300 Subject: [PATCH 20/37] Add set_flash method --- app/controllers/application_controller.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1b8dc55..9056e36 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,6 +10,12 @@ class ApplicationController < ActionController::Base before_action :set_js_current_user + protected + + def set_flash(scope, message) + flash[scope] = message if is_navigational_format? + end + private def set_js_current_user From 4a9c089248185f792b75d6891b945010de9ff1d5 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 02:56:06 +0300 Subject: [PATCH 21/37] Minor model changes --- app/models/identity.rb | 4 ++-- app/models/user.rb | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/identity.rb b/app/models/identity.rb index efce3a8..0937cd3 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,6 +1,6 @@ class Identity < ActiveRecord::Base belongs_to :user - validates :user_id, :uid, :provider, presence: true - validates :uid, uniqueness: {scope: :provider} + validates :user_id, :provider, presence: true + validates :uid, presence: true, uniqueness: {scope: :provider} end diff --git a/app/models/user.rb b/app/models/user.rb index 3485ae2..de6b253 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -17,11 +17,16 @@ def find_for_oauth(auth) return identity.user if identity email = auth.info[:email] + return nil unless email + user = User.where(email: email).first unless user password = Devise.friendly_token(20) - user = User.create!(email: email, password: password, password_confirmation: password) + user = User.new(email: email, password: password, password_confirmation: password) + # user.skip_confirmation! if auth.provider == 'facebook' + user.save end + user.identities.create(provider: auth.provider, uid: auth.uid) if user user end From 4b315d7c0d6ef7e1f0facfb259258e61f7311b6d Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 02:59:36 +0300 Subject: [PATCH 22/37] Change originator email to e@varnakov.ru --- config/initializers/devise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 05f8a79..7783578 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -12,7 +12,7 @@ # Configure the e-mail address which will be shown in Devise::Mailer, # note that it will be overwritten if you use your own mailer class # with default "from" parameter. - config.mailer_sender = 'gene+deeprecursion@deep.msk.ru' + config.mailer_sender = 'e@varnakov.ru' # Configure the class responsible to send e-mails. config.mailer = 'Devise::Mailer' From 4a13d8fbbb77eddd23e29bf57bb74d75b0da635b Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 03:00:28 +0300 Subject: [PATCH 23/37] Identity factory change to work with sequences --- spec/factories/identities.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb index c5fa824..54522bc 100644 --- a/spec/factories/identities.rb +++ b/spec/factories/identities.rb @@ -1,14 +1,16 @@ FactoryGirl.define do - factory :identity do - user - provider 'deeprec' - uid 'eehieWijeshingaet8ch' + sequence :uid do |n| + "#{Time.now.to_i}-#{rand(9000) + 1000}-#{n}" end - factory :alt_identity, class: 'Identity' do + sequence :provider do |n| + "deeprec-provider-#{n}" + end + + factory :identity do user - provider 'theotherprovider' - uid 'dengahpo4tae8Iew1eem' + provider + uid end factory :invalid_identity, class: 'Identity' do From 57e6c192c960d0f5c281430812646b3c2988f231 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 03:02:49 +0300 Subject: [PATCH 24/37] Accounts controller (to confirm email) w/spec --- app/controllers/account_controller.rb | 46 +++++++ spec/controllers/account_controller_spec.rb | 125 ++++++++++++++++++-- 2 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 app/controllers/account_controller.rb diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb new file mode 100644 index 0000000..e7c27d4 --- /dev/null +++ b/app/controllers/account_controller.rb @@ -0,0 +1,46 @@ +class AccountController < ApplicationController + before_action :check_already_signed_in, only: [:confirm_email] + before_action :check_oauth_in_session, only: [:confirm_email] + + def confirm_email + if request.patch? + email = params[:email] + user = User.where(email: email).first + provider = session['devise.oauth_provider'] + if user.present? + set_flash :alert, 'Email already taken.' + else + User.transaction do + @user = User.create(email: email, password: Devise.friendly_token[0, 20]) + if @user.persisted? + @user.identities.create!({ + provider: provider, + uid: session['devise.oauth_uid'].to_s}) + end + end + if @user.persisted? + set_flash :notice, t(:success, kind: provider.to_s.capitalize) + sign_in_and_redirect @user, event: :authentication + end + end + end + end + + private + + def check_already_signed_in + redirect_to questions_path if signed_in? + end + + def check_oauth_in_session + if !session['devise.oauth_provider'] && !session['devise.oauth_uid'] + set_flash :notice, 'Registration done' + redirect_to questions_path + elsif !session['devise.oauth_provider'] || !session['devise.oauth_uid'] + session['devise.oauth_provider'] = nil + session['devise.oauth_uid'] = nil + set_flash :alert, 'Authentication error, try again' + redirect_to new_user_session_path + end + end +end diff --git a/spec/controllers/account_controller_spec.rb b/spec/controllers/account_controller_spec.rb index 3f51683..12ed93c 100644 --- a/spec/controllers/account_controller_spec.rb +++ b/spec/controllers/account_controller_spec.rb @@ -1,23 +1,130 @@ require 'rails_helper' describe AccountController do - let (:user) { create(:user) } + let!(:user) { create(:user) } describe 'GET #confirm_email' do + subject { get :confirm_email } + context 'as unauthenticated user' do - context 'with valid OAuth data in session' - context 'with invalid OAuth data in session' - context 'with no OAuth data in session' + context 'with valid OAuth data in session' do + before do + session['devise.oauth_provider'] = generate(:provider) + session['devise.oauth_uid'] = generate(:uid) + end + + it { is_expected.to have_http_status :success } + it { is_expected.to render_template :confirm_email } + end + + context 'with invalid OAuth data in session' do + before do + session['devise.oauth_provider'] = generate(:provider) + session['devise.uid'] = nil + end + + it { is_expected.to have_http_status :redirect } + it { is_expected.to redirect_to new_user_session_path } + end + + context 'with no OAuth data in session' do + it { is_expected.to have_http_status :redirect } + it { is_expected.to redirect_to questions_path } + end + end + + context 'as authenticated user' do + sign_in_user + + before { get :confirm_email } + + it { is_expected.to have_http_status :redirect } + it { is_expected.to redirect_to questions_path } end - context 'as authenticated user' end describe 'PATCH #confirm_email' do + let(:email) { generate(:email) } + subject (:confirm_email) { patch :confirm_email, email: email } + context 'as unauthenticated user' do - context 'with valid OAuth data in session' - context 'with invalid OAuth data in session' - context 'with no OAuth data in session' + context 'with valid OAuth data in session' do + before do + session['devise.oauth_provider'] = generate(:provider) + session['devise.oauth_uid'] = generate(:uid) + end + + context 'with correct email' do + it { is_expected.to have_http_status :redirect } + + it 'should assign User object to @user' do + confirm_email + expect(assigns(:user)).to be_a User + end + + context 'for new user' do + it 'should create new User instance' do + expect { confirm_email }.to change(User, :count).by(1) + end + + it 'should create new Identity for the User' do + expect { confirm_email }.to change(Identity, :count).by(1) + end + + it 'should set given email for new User' do + confirm_email + expect(assigns(:user).email).to eq email + end + end + + context 'for existing user' do + subject(:confirm_email) { patch :confirm_email, email: user.email } + + it 'shouldn\'t create new User instance' do + expect { confirm_email }.not_to change(User, :count) + end + + it 'shouldn\'t create new Identitities' do + expect { confirm_email }.not_to change(Identity, :count) + end + end + end + + context 'with invalid (empty) email' do + subject(:confirm_email) { patch :confirm_email } + + it { is_expected.to have_http_status :success } + it { is_expected.to render_template :confirm_email } + + it 'shouldn\'t create new Identitities' do + expect { confirm_email }.not_to change(Identity, :count) + end + end + end + + context 'with invalid OAuth data in session' do + before do + session['devise.oauth_provider'] = generate(:provider) + session['devise.uid'] = nil + end + + it { is_expected.to have_http_status :redirect } + it { is_expected.to redirect_to new_user_session_path } + end + + context 'with no OAuth data in session' do + it { is_expected.to have_http_status :redirect } + it { is_expected.to redirect_to questions_path } + end + end + + context 'as authenticated user' do + sign_in_user + + before { get :confirm_email } + + it { is_expected.to have_http_status :redirect } + it { is_expected.to redirect_to questions_path } end - context 'as authenticated user' end end From bc9c1b5f4d55da19dfe958c40512d0833744ab6c Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 03:04:01 +0300 Subject: [PATCH 25/37] Draft of omniauth_callbacks controller w/spec --- .../omniauth_callbacks_controller.rb | 42 ++++++++++--- .../omniauth_callbacks_controller.rb | 62 +++++++++++++++++++ 2 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 spec/controllers/omniauth_callbacks_controller.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index eb20de1..60765d3 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,17 +1,41 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController + before_action :oauth + def facebook - @user = User.find_for_oauth(request.env['omniauth.auth']) - if @user.persisted? - sign_in_and_redirect @user, event: :authentication - set_flash_message :notice, :success, kind: 'Facebook' if is_navigational_format? - end end def twitter - @user = User.find_for_oauth(request.env['omniauth.auth']) - if @user.persisted? - sign_in_and_redirect @user, event: :authentication - set_flash_message :notice, :success, kind: 'Twitter' if is_navigational_format? + end + + private + + def oauth + auth = request.env['omniauth.auth'] || OmniAuth::AuthHash.new( + provider: session['oauth_provider'], + uid: session['oauth_uid'], + info: {email: params[:email]}) + if auth + @user = User.find_for_oauth(auth) + if @user.nil? && auth && auth.provider && auth.uid + store_auth auth + redirect_to 'user/email' + elsif @user&.persisted? + set_flash_message :notice, :success, kind: auth.provider.to_s.capitalize if is_navigational_format? + sign_in_and_redirect @user, event: :authentication + else + failure_redirect + end + else + failure_redirect end end + + def store_auth(auth) + session['devise.oauth_provider'] = auth.provider + session['devise.oauth_uid'] = auth.uid + end + + def failure_redirect + redirect_to new_user_registration_path, alert: 'Authentication failure' + end end diff --git a/spec/controllers/omniauth_callbacks_controller.rb b/spec/controllers/omniauth_callbacks_controller.rb new file mode 100644 index 0000000..2e8d622 --- /dev/null +++ b/spec/controllers/omniauth_callbacks_controller.rb @@ -0,0 +1,62 @@ +require 'rails_helper' + +shared_examples :oauth_callback do + let(:user) { create(:user) } + before { request.env['devise.mapping'] = Devise.mappings[:user] } + + + context 'empty call' do + subject { get provider } + it { is_expected.to redirect_to new_user_registration_path } + # if { is_expected.to show_flas} + end + + context 'email is nil' do + before do + request.env['omniauth.auth'] = OmniAuth::AuthHash.new(provider: provider.to_s, uid: '123456', info: {email: nil}) + get provider + end + + it 'stores data in session' do + expect(session['devise.oauth_provider']).to eq provider.to_s + expect(session['devise.oauth_uid']).to eq '123456' + end + + it { should_not be_user_signed_in } + it { expect(response).to redirect_to 'user/email' } + end + + context 'user does not exist' do + before do + request.env['omniauth.auth'] = OmniAuth::AuthHash.new(provider: provider.to_s, uid: '123456', info: {email: 'new-user@email.com'}) + get provider + end + + it 'assigns user to @user' do + expect(assigns(:user)).to be_a(User) + end + it 'expects to send confirmation email' + end + + context 'everything is nil' do + before do + request.env['omniauth.auth'] = OmniAuth::AuthHash.new(provider: nil, uid: nil, info: {email: nil}) + get provider + end + + it 'redirect to registration' do + expect(response).to redirect_to new_user_registration_path + end + end +end + +describe OmniauthCallbacksController do + + [:facebook, :twitter].each do |provider| + describe "GET ##{provider}" do + include_examples :oauth_callback do + let(:provider) { provider } + end + end + end +end \ No newline at end of file From b09b26eb52c17c45fcbc12fccbba9c3a398892c6 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 03:26:23 +0300 Subject: [PATCH 26/37] Fix configuration to correct handling of emails at test env --- config/environments/test.rb | 1 + config/initializers/devise.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/config/environments/test.rb b/config/environments/test.rb index 1c19f08..03c76e7 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -30,6 +30,7 @@ # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } # Randomize the order test cases are executed. config.active_support.test_order = :random diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 7783578..b7a931a 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -114,6 +114,7 @@ # access will be blocked just in the third day. Default is 0.days, meaning # the user cannot access the website without confirming their account. # config.allow_unconfirmed_access_for = 2.days + config.allow_unconfirmed_access_for = 2.days if Rails.env.test? # A period that the user is allowed to confirm their account before their # token becomes invalid. For example, if set to 3.days, the user can confirm From 34e6f71bdb11355b410c7ab20bd3eeb525bf2312 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 03:28:01 +0300 Subject: [PATCH 27/37] Fix Identity model spec; Make it comaptible with case-sensitive validation tests --- spec/factories/identities.rb | 2 +- spec/models/identity_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb index 54522bc..9da8a38 100644 --- a/spec/factories/identities.rb +++ b/spec/factories/identities.rb @@ -1,6 +1,6 @@ FactoryGirl.define do sequence :uid do |n| - "#{Time.now.to_i}-#{rand(9000) + 1000}-#{n}" + "#{Time.now.to_i}-#{Faker::Lorem.word}-#{n}" end sequence :provider do |n| diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb index 3c54054..e065c24 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -8,7 +8,7 @@ it { is_expected.to validate_presence_of :provider } describe 'should validate uniqueness of uid for the provider' do - subject { create(:alt_identity) } + subject { create(:identity) } it { is_expected.to validate_uniqueness_of(:uid).scoped_to(:provider) } end end From bbecfa77ec0a5c606a82959a3091e4ccec0ad8a7 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Tue, 6 Sep 2016 03:39:16 +0300 Subject: [PATCH 28/37] Temporary feature spec for OAuth sign in --- .../features/{sign_in_oauth.rb => sign_in_oauth_spec.rb} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename spec/features/{sign_in_oauth.rb => sign_in_oauth_spec.rb} (83%) diff --git a/spec/features/sign_in_oauth.rb b/spec/features/sign_in_oauth_spec.rb similarity index 83% rename from spec/features/sign_in_oauth.rb rename to spec/features/sign_in_oauth_spec.rb index ba915f0..429c795 100644 --- a/spec/features/sign_in_oauth.rb +++ b/spec/features/sign_in_oauth_spec.rb @@ -1,12 +1,11 @@ require 'features_helper' - shared_examples :oauth_sign_in do given(:user) { create(:user) } + given(:provider_name) { provider.to_s.capitalize } - context 'with email' do - scenario 'do new registraion' do - provider_name = provider.to_s.capitalize + context 'with email given from OAuth provider' do + scenario 'sign up new user with OAuth' do link_text = "Sign in with #{provider_name}" visit new_user_session_path expect(page).to have_link link_text @@ -32,7 +31,7 @@ end # context 'Logging in with Twitter OAuth' do - # given(:provider) { :twitter } + # given!(:provider) { :twitter } # given!(:identity) { create(:identity, provider: provider) } # # it_behaves_like :oauth_sign_in From b0bde33a8098d675b4158c7708e3a560ac1f7957 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 Sep 2016 15:34:33 +0300 Subject: [PATCH 29/37] Cleanup omniauth_macros.rb --- spec/support/omniauth_macros.rb | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/spec/support/omniauth_macros.rb b/spec/support/omniauth_macros.rb index 809f8de..056fc9a 100644 --- a/spec/support/omniauth_macros.rb +++ b/spec/support/omniauth_macros.rb @@ -3,16 +3,7 @@ def mock_auth_hash(provider, oauth_params = {}) OmniAuth.config.mock_auth[provider] = OmniAuth::AuthHash.new( { provider: provider, - uid: '123545', - info: {email: 'someuser@somehost.dom'} - # 'user_info' => { - # 'name' => 'mockuser', - # 'image' => 'mock_user_thumbnail_url' - # }, - # 'credentials' => { - # 'token' => 'mock_token', - # 'secret' => 'mock_secret' - # } + uid: '123545' }.merge oauth_params) end end From d31f6e80097bfbb2001a5aa9daeffebd1485b360 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 Sep 2016 16:31:11 +0300 Subject: [PATCH 30/37] Add real email validation route --- app/controllers/omniauth_callbacks_controller.rb | 2 +- spec/controllers/omniauth_callbacks_controller.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 60765d3..d12b991 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -18,7 +18,7 @@ def oauth @user = User.find_for_oauth(auth) if @user.nil? && auth && auth.provider && auth.uid store_auth auth - redirect_to 'user/email' + redirect_to account_confirm_email_path elsif @user&.persisted? set_flash_message :notice, :success, kind: auth.provider.to_s.capitalize if is_navigational_format? sign_in_and_redirect @user, event: :authentication diff --git a/spec/controllers/omniauth_callbacks_controller.rb b/spec/controllers/omniauth_callbacks_controller.rb index 2e8d622..13d5816 100644 --- a/spec/controllers/omniauth_callbacks_controller.rb +++ b/spec/controllers/omniauth_callbacks_controller.rb @@ -23,7 +23,7 @@ end it { should_not be_user_signed_in } - it { expect(response).to redirect_to 'user/email' } + it { expect(response).to redirect_to account_confirm_email_path } end context 'user does not exist' do @@ -59,4 +59,4 @@ end end end -end \ No newline at end of file +end From eeb30a6c5a5d79b813bcdc98c2391fab14524c04 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 Sep 2016 16:32:35 +0300 Subject: [PATCH 31/37] Add display of email validation errors, fix parameter name --- app/controllers/account_controller.rb | 2 +- app/views/account/confirm_email.slim | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index e7c27d4..922f249 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -4,7 +4,7 @@ class AccountController < ApplicationController def confirm_email if request.patch? - email = params[:email] + email = params[:user][:email] user = User.where(email: email).first provider = session['devise.oauth_provider'] if user.present? diff --git a/app/views/account/confirm_email.slim b/app/views/account/confirm_email.slim index 5366133..f5bb479 100644 --- a/app/views/account/confirm_email.slim +++ b/app/views/account/confirm_email.slim @@ -1,7 +1,8 @@ h2 Enter your email to proceed .row - .col-sm-12#email-errors + .col-sm-12 + = render 'shared/form_errors', object: @user .row .col-sm-12 @@ -11,4 +12,4 @@ h2 Enter your email to proceed = f.email_field :email, class: 'form-control', placeholder: 'enter@email.here' /= email_field_tag :email, params[:email], class: 'form-control', placeholder: 'enter@email.here' .form-group - = f.submit 'Validate Email', class: 'btn btn-primary pull-right' \ No newline at end of file + = f.submit 'Validate Email', class: 'btn btn-primary pull-right' From 0f728f85ee9e819cabc171f6985e6b39d297e06d Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 Sep 2016 18:47:40 +0300 Subject: [PATCH 32/37] Add Twitter OAuth to Devise config --- config/initializers/devise.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index b7a931a..bc19a1e 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -245,6 +245,8 @@ # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' config.omniauth :facebook, Rails.application.secrets.facebook_app_id, Rails.application.secrets.facebook_app_secret, scope: [:email] + config.omniauth :twitter, Rails.application.secrets.twitter_app_id, + Rails.application.secrets.twitter_app_secret # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or From 2114cd12bf792b7a75ae9a659fd82a50cd4b28d8 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 Sep 2016 18:49:15 +0300 Subject: [PATCH 33/37] OAuth sign in feature spec; many fixes --- app/controllers/account_controller.rb | 5 +- .../omniauth_callbacks_controller.rb | 4 +- app/models/user.rb | 5 +- app/views/shared/_form_errors.slim | 2 +- spec/controllers/account_controller_spec.rb | 2 +- ... => omniauth_callbacks_controller_spec.rb} | 12 +- spec/features/sign_in_oauth_spec.rb | 114 +++++++++++++++--- 7 files changed, 115 insertions(+), 29 deletions(-) rename spec/controllers/{omniauth_callbacks_controller.rb => omniauth_callbacks_controller_spec.rb} (87%) diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 922f249..6d8c034 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -4,7 +4,8 @@ class AccountController < ApplicationController def confirm_email if request.patch? - email = params[:user][:email] + email = nil + email = params[:user][:email] if params[:user] && params[:user][:email] user = User.where(email: email).first provider = session['devise.oauth_provider'] if user.present? @@ -19,7 +20,7 @@ def confirm_email end end if @user.persisted? - set_flash :notice, t(:success, kind: provider.to_s.capitalize) + set_flash :notice, t('devise.omniauth_callbacks.success', kind: provider.to_s.capitalize) sign_in_and_redirect @user, event: :authentication end end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index d12b991..5fc32bf 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -10,10 +10,12 @@ def twitter private def oauth + email = nil + email = params[:user][:email] if params[:user] && params[:user][:email] auth = request.env['omniauth.auth'] || OmniAuth::AuthHash.new( provider: session['oauth_provider'], uid: session['oauth_uid'], - info: {email: params[:email]}) + info: {email: email}) if auth @user = User.find_for_oauth(auth) if @user.nil? && auth && auth.provider && auth.uid diff --git a/app/models/user.rb b/app/models/user.rb index de6b253..545e96a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,17 +13,18 @@ class User < ActiveRecord::Base class << self def find_for_oauth(auth) + return nil unless auth + identity = Identity.where(provider: auth.provider, uid: auth.uid.to_s).first return identity.user if identity - email = auth.info[:email] + email = auth.info[:email] if auth.info return nil unless email user = User.where(email: email).first unless user password = Devise.friendly_token(20) user = User.new(email: email, password: password, password_confirmation: password) - # user.skip_confirmation! if auth.provider == 'facebook' user.save end diff --git a/app/views/shared/_form_errors.slim b/app/views/shared/_form_errors.slim index 4df8c99..dc05cf1 100644 --- a/app/views/shared/_form_errors.slim +++ b/app/views/shared/_form_errors.slim @@ -1,4 +1,4 @@ -- if object.errors.any? +- if object&.errors&.any? #error_explanation .alert.alert-danger p.lead Form has an error(s): diff --git a/spec/controllers/account_controller_spec.rb b/spec/controllers/account_controller_spec.rb index 12ed93c..40ede5c 100644 --- a/spec/controllers/account_controller_spec.rb +++ b/spec/controllers/account_controller_spec.rb @@ -45,7 +45,7 @@ describe 'PATCH #confirm_email' do let(:email) { generate(:email) } - subject (:confirm_email) { patch :confirm_email, email: email } + subject (:confirm_email) { patch :confirm_email, user: {email: email} } context 'as unauthenticated user' do context 'with valid OAuth data in session' do diff --git a/spec/controllers/omniauth_callbacks_controller.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb similarity index 87% rename from spec/controllers/omniauth_callbacks_controller.rb rename to spec/controllers/omniauth_callbacks_controller_spec.rb index 13d5816..ba8968a 100644 --- a/spec/controllers/omniauth_callbacks_controller.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -5,13 +5,12 @@ before { request.env['devise.mapping'] = Devise.mappings[:user] } - context 'empty call' do + context 'empty request' do subject { get provider } it { is_expected.to redirect_to new_user_registration_path } - # if { is_expected.to show_flas} end - context 'email is nil' do + context 'email not given' do before do request.env['omniauth.auth'] = OmniAuth::AuthHash.new(provider: provider.to_s, uid: '123456', info: {email: nil}) get provider @@ -38,20 +37,19 @@ it 'expects to send confirmation email' end - context 'everything is nil' do + context 'nulled credentials' do before do - request.env['omniauth.auth'] = OmniAuth::AuthHash.new(provider: nil, uid: nil, info: {email: nil}) + request.env['omniauth.auth'] = OmniAuth::AuthHash.new(provider: nil, uid: nil) get provider end - it 'redirect to registration' do + it 'redirects to registration' do expect(response).to redirect_to new_user_registration_path end end end describe OmniauthCallbacksController do - [:facebook, :twitter].each do |provider| describe "GET ##{provider}" do include_examples :oauth_callback do diff --git a/spec/features/sign_in_oauth_spec.rb b/spec/features/sign_in_oauth_spec.rb index 429c795..37b36bd 100644 --- a/spec/features/sign_in_oauth_spec.rb +++ b/spec/features/sign_in_oauth_spec.rb @@ -4,17 +4,101 @@ given(:user) { create(:user) } given(:provider_name) { provider.to_s.capitalize } - context 'with email given from OAuth provider' do - scenario 'sign up new user with OAuth' do - link_text = "Sign in with #{provider_name}" - visit new_user_session_path - expect(page).to have_link link_text - mock_auth_hash(provider, {info: { email: user.email}}) - click_link link_text - auth_success_text = "GLYPH:info-sign Successfully authenticated from #{provider_name} account." - expect(page).to have_text auth_success_text + let(:link_text) { "Sign in with #{provider_name}" } + let(:success_auth_text) { "GLYPH:info-sign Successfully authenticated from #{provider_name} account." } + + subject(:sign_in_with_oauth) do + visit new_user_session_path + expect(page).to have_link link_text + click_link link_text + end + + context 'when email is sent by OAuth provider' do + scenario 'when User and Identity are new' do + mock_auth_hash(provider, {info: {email: user.email}}) + sign_in_with_oauth + + expect(page).to have_content success_auth_text + end + + scenario 'when User with given email is already registered' do + mock_auth_hash(identity.provider, info: {email: user.email}) + sign_in_with_oauth + + expect(page).to have_content success_auth_text + expect(page).not_to have_content 'Enter your email to proceed' + end + + scenario 'when Identity is already registered with given email' do + mock_auth_hash(identity.provider, uid: identity.uid, info: {email: identity.user.email}) + sign_in_with_oauth + + expect(page).to have_content success_auth_text end end + + context 'when no email is sent by OAuth provider' do + context 'when User and Identity are new' do + before do + mock_auth_hash(provider, info: nil) + sign_in_with_oauth + + expect(page).not_to have_content success_auth_text + expect(page).to have_content 'Enter your email to proceed' + expect(page).to have_button 'Validate Email' + end + + scenario 'with new valid email' do + fill_in 'Email', with: generate(:email) + click_on 'Validate Email' + + expect(page).to have_content success_auth_text + end + + scenario 'with email that already exists' do + fill_in 'Email', with: user.email + click_on 'Validate Email' + + expect(page).not_to have_content success_auth_text + expect(page).to have_content 'GLYPH:alert Email already taken' + end + + scenario 'with empty email' do + click_on 'Validate Email' + + expect(page).not_to have_content success_auth_text + expect(page).to have_content 'Form has an error(s)' + expect(page).to have_content 'Email can\'t be blank' + end + + scenario 'with invalid email' do + fill_in 'Email', with: 'this_is_really_not_an_email' + click_on 'Validate Email' + + expect(page).not_to have_content success_auth_text + expect(page).to have_content 'Form has an error(s)' + expect(page).to have_content 'Email is invalid' + end + end + end + + scenario 'with invalid credentials' do + OmniAuth.config.mock_auth[provider] = :invalid_credentials + sign_in_with_oauth + + expect(page).not_to have_content success_auth_text + expect(page).to have_content 'GLYPH:alert Authentication failure' + end + + scenario 'when User already logged in' do + sign_in user + + visit new_user_session_path + + expect(page).not_to have_content success_auth_text + expect(page).not_to have_link link_text + expect(page).to have_content 'GLYPH:alert You are already signed in.' + end end feature 'Signing in using OAuth', %q( @@ -30,10 +114,10 @@ it_behaves_like :oauth_sign_in end - # context 'Logging in with Twitter OAuth' do - # given!(:provider) { :twitter } - # given!(:identity) { create(:identity, provider: provider) } - # - # it_behaves_like :oauth_sign_in - # end + context 'Logging in with Twitter OAuth' do + given!(:provider) { :twitter } + given!(:identity) { create(:identity, provider: provider) } + + it_behaves_like :oauth_sign_in + end end From d421fcf7e09aff9fe1ca5039948aad2e4acb3d27 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 Sep 2016 18:52:43 +0300 Subject: [PATCH 34/37] More DRY OAuth sign in spec --- spec/features/sign_in_oauth_spec.rb | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/spec/features/sign_in_oauth_spec.rb b/spec/features/sign_in_oauth_spec.rb index 37b36bd..0d539bd 100644 --- a/spec/features/sign_in_oauth_spec.rb +++ b/spec/features/sign_in_oauth_spec.rb @@ -107,17 +107,12 @@ I want to sign in using Facebook or Twitter ) do - context 'Logging in with Facebook OAuth' do - given!(:provider) { :facebook } - given!(:identity) { create(:identity, provider: provider) } + [:facebook, :twitter].each do |prov| + context "Logging in with #{prov.to_s.capitalize} OAuth" do + given!(:provider) { prov } + given!(:identity) { create(:identity, provider: provider) } - it_behaves_like :oauth_sign_in - end - - context 'Logging in with Twitter OAuth' do - given!(:provider) { :twitter } - given!(:identity) { create(:identity, provider: provider) } - - it_behaves_like :oauth_sign_in + it_behaves_like :oauth_sign_in + end end end From a5d443443f0ec92f199825fedbe2b3cb274b242a Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 6 Sep 2016 20:02:08 +0300 Subject: [PATCH 35/37] Try to check emails in specs --- config/environments/test.rb | 2 +- .../omniauth_callbacks_controller_spec.rb | 1 - .../confirm_oauth_registration_spec.rb | 43 +++++++++++++++++++ spec/features_helper.rb | 3 ++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 spec/features/confirm_oauth_registration_spec.rb diff --git a/config/environments/test.rb b/config/environments/test.rb index 03c76e7..124e18e 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -30,7 +30,7 @@ # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } + config.action_mailer.default_url_options = { host: 'localhost', port: 3001 } # Randomize the order test cases are executed. config.active_support.test_order = :random diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index ba8968a..9f4e5ff 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -34,7 +34,6 @@ it 'assigns user to @user' do expect(assigns(:user)).to be_a(User) end - it 'expects to send confirmation email' end context 'nulled credentials' do diff --git a/spec/features/confirm_oauth_registration_spec.rb b/spec/features/confirm_oauth_registration_spec.rb new file mode 100644 index 0000000..8ad179c --- /dev/null +++ b/spec/features/confirm_oauth_registration_spec.rb @@ -0,0 +1,43 @@ +require 'features_helper' + +shared_examples :oauth_registration_confirm do + let(:email) { '1234@ab536356c.com' } + + background do + clear_emails + mock_auth_hash(provider, info: nil) + visit new_user_session_path + click_on "Sign in with #{provider.to_s.capitalize}" + sleep 1 + fill_in 'Email', with: email + click_on "Validate Email" + sleep 1 + save_and_open_page + puts '--------------------------------' + p ActionMailer::Base.deliveries + p all_emails + p current_email + open_email "test@example.com" + end + + scenario 'check email' do + current_email.click_link 'Confirm my account' + expect(page).to have_content 'Your email address has been successfully confirmed' + end +end + +feature 'Confirm OAuth registration', %q( + To became regular user + As a guest having Facebook or Twitter account + I want to set and confirm my email +) do + + [:facebook, :twitter].each do |prov| + context "Logging in with #{prov.to_s.capitalize} OAuth" do + given!(:provider) { prov } + # given!(:identity) { create(:identity, provider: provider) } + + it_behaves_like :oauth_registration_confirm + end + end +end diff --git a/spec/features_helper.rb b/spec/features_helper.rb index 1a7e348..3d0faf9 100644 --- a/spec/features_helper.rb +++ b/spec/features_helper.rb @@ -12,6 +12,9 @@ config.block_unknown_urls end + Capybara.server_port = 3001 + Capybara.app_host = 'http://localhost:3001' + config.include FeaturesMacros, type: :feature config.use_transactional_fixtures = false From f3d2ad766567edadf4d55c0a6981d3abee4e9c42 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 7 Sep 2016 14:50:33 +0300 Subject: [PATCH 36/37] Temporary disable checks in confirm registration spec --- spec/features/confirm_oauth_registration_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/confirm_oauth_registration_spec.rb b/spec/features/confirm_oauth_registration_spec.rb index 8ad179c..708a85e 100644 --- a/spec/features/confirm_oauth_registration_spec.rb +++ b/spec/features/confirm_oauth_registration_spec.rb @@ -20,10 +20,10 @@ open_email "test@example.com" end - scenario 'check email' do - current_email.click_link 'Confirm my account' - expect(page).to have_content 'Your email address has been successfully confirmed' - end + # scenario 'check email' do + # current_email.click_link 'Confirm my account' + # expect(page).to have_content 'Your email address has been successfully confirmed' + # end end feature 'Confirm OAuth registration', %q( From 71d59ad17b0831d5c2a519a3c689a04e01fd9b27 Mon Sep 17 00:00:00 2001 From: Evgeny Varnakov Date: Sat, 10 Sep 2016 22:07:55 +0300 Subject: [PATCH 37/37] Still trying to check mails in RSpec. With no luck yet. --- Gemfile | 3 ++- config/environments/test.rb | 7 +++++++ spec/features/devise_email_spec.rb | 26 ++++++++++++++++++++++++++ spec/features_helper.rb | 1 - spec/rails_helper.rb | 1 + 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 spec/features/devise_email_spec.rb diff --git a/Gemfile b/Gemfile index 5c85e11..82d12eb 100644 --- a/Gemfile +++ b/Gemfile @@ -45,9 +45,10 @@ group :test do gem 'launchy' gem 'database_cleaner' gem 'capybara-webkit' - gem 'capybara-email' end +gem 'capybara-email' + group :development do gem 'web-console', '~> 2.0' gem 'pry' diff --git a/config/environments/test.rb b/config/environments/test.rb index 124e18e..aecc6eb 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -30,6 +30,7 @@ # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + config.action_mailer.perform_deliveries = true config.action_mailer.default_url_options = { host: 'localhost', port: 3001 } # Randomize the order test cases are executed. @@ -40,4 +41,10 @@ # Raises error for missing translations # config.action_view.raise_on_missing_translations = true + + # Enable stdout logger + config.logger = Logger.new(STDOUT) + + # Set log level + config.log_level = :DEBUG end diff --git a/spec/features/devise_email_spec.rb b/spec/features/devise_email_spec.rb new file mode 100644 index 0000000..20ec06a --- /dev/null +++ b/spec/features/devise_email_spec.rb @@ -0,0 +1,26 @@ +require 'features_helper' + +feature 'Check devise confirmation emails works' do + background do + # will clear the message queue + clear_emails + p Devise::Mailer.logger + p Devise::Mailer.perform_deliveries + p Devise::Mailer.delivery_method + + User.create(email: 'test@example.com', password: '123456') + # Will find an email sent to test@example.com + # and set `current_email` + sleep 1 + p ActionMailer::Base.deliveries + p all_emails + open_email('test@example.com') + p current_email + end + + scenario 'following a link' do + p all_emails + # current_email.click_link 'your profile' + # expect(page).to have_content 'Profile page' + end +end diff --git a/spec/features_helper.rb b/spec/features_helper.rb index 3d0faf9..5fada1b 100644 --- a/spec/features_helper.rb +++ b/spec/features_helper.rb @@ -1,6 +1,5 @@ require 'rails_helper' require 'tilt/coffee' -require 'capybara/email/rspec' RSpec.configure do |config| config.include(OmniauthMacros) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 8358237..bf5e6b8 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -6,6 +6,7 @@ require 'spec_helper' require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! +require 'capybara/email/rspec' # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are