Commit 361a8781c939e96c9718403db5330f389f13e64b

Authored by Dmitriy Zaporozhets
2 parents 66d21d54 205abf22

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

app/controllers/profiles/emails_controller.rb 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +class Profiles::EmailsController < ApplicationController
  2 + layout "profile"
  3 +
  4 + def index
  5 + @primary = current_user.email
  6 + @emails = current_user.emails
  7 + end
  8 +
  9 + def create
  10 + @email = current_user.emails.new(params[:email])
  11 +
  12 + flash[:alert] = @email.errors.full_messages.first unless @email.save
  13 +
  14 + redirect_to profile_emails_url
  15 + end
  16 +
  17 + def destroy
  18 + @email = current_user.emails.find(params[:id])
  19 + @email.destroy
  20 +
  21 + respond_to do |format|
  22 + format.html { redirect_to profile_emails_url }
  23 + format.js { render nothing: true }
  24 + end
  25 + end
  26 +end
... ...
app/helpers/commits_helper.rb
... ... @@ -122,17 +122,18 @@ module CommitsHelper
122 122 def commit_person_link(commit, options = {})
123 123 source_name = commit.send "#{options[:source]}_name".to_sym
124 124 source_email = commit.send "#{options[:source]}_email".to_sym
  125 +
  126 + user = User.find_for_commit(source_email, source_name)
  127 + person_name = user.nil? ? source_name : user.name
  128 + person_email = user.nil? ? source_email : user.email
  129 +
125 130 text = if options[:avatar]
126   - avatar = image_tag(avatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
127   - %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
  131 + avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
  132 + %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
128 133 else
129   - source_name
  134 + person_name
130 135 end
131 136  
132   - # Prefer email match over name match
133   - user = User.where(email: source_email).first
134   - user ||= User.where(name: source_name).first
135   -
136 137 options = {
137 138 class: "commit-#{options[:source]}-link has_tooltip",
138 139 data: { :'original-title' => sanitize(source_email) }
... ...
app/mailers/emails/profile.rb
... ... @@ -6,6 +6,12 @@ module Emails
6 6 mail(to: @user.email, subject: subject("Account was created for you"))
7 7 end
8 8  
  9 + def new_email_email(email_id)
  10 + @email = Email.find(email_id)
  11 + @user = @email.user
  12 + mail(to: @user.email, subject: subject("Email was added to your account"))
  13 + end
  14 +
9 15 def new_ssh_key_email(key_id)
10 16 @key = Key.find(key_id)
11 17 @user = @key.user
... ...
app/models/email.rb 0 → 100644
... ... @@ -0,0 +1,33 @@
  1 +# == Schema Information
  2 +#
  3 +# Table name: emails
  4 +#
  5 +# id :integer not null, primary key
  6 +# user_id :integer not null
  7 +# email :string not null
  8 +# created_at :datetime not null
  9 +class Email < ActiveRecord::Base
  10 + attr_accessible :email, :user_id
  11 +
  12 + #
  13 + # Relations
  14 + #
  15 + belongs_to :user
  16 +
  17 + #
  18 + # Validations
  19 + #
  20 + validates :user_id, presence: true
  21 + validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
  22 + validate :unique_email, if: ->(email) { email.email_changed? }
  23 +
  24 + before_validation :cleanup_email
  25 +
  26 + def cleanup_email
  27 + self.email = self.email.downcase.strip
  28 + end
  29 +
  30 + def unique_email
  31 + self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email)
  32 + end
  33 +end
0 34 \ No newline at end of file
... ...
app/models/user.rb
... ... @@ -78,6 +78,7 @@ class User &lt; ActiveRecord::Base
78 78  
79 79 # Profile
80 80 has_many :keys, dependent: :destroy
  81 + has_many :emails, dependent: :destroy
81 82  
82 83 # Groups
83 84 has_many :users_groups, dependent: :destroy
... ... @@ -116,6 +117,7 @@ class User &lt; ActiveRecord::Base
116 117 validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
117 118 validate :namespace_uniq, if: ->(user) { user.username_changed? }
118 119 validate :avatar_type, if: ->(user) { user.avatar_changed? }
  120 + validate :unique_email, if: ->(user) { user.email_changed? }
119 121 validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
120 122  
121 123 before_validation :generate_password, on: :create
... ... @@ -183,6 +185,13 @@ class User &lt; ActiveRecord::Base
183 185 where(conditions).first
184 186 end
185 187 end
  188 +
  189 + def find_for_commit(email, name)
  190 + # Prefer email match over name match
  191 + User.where(email: email).first ||
  192 + User.joins(:emails).where(emails: { email: email }).first ||
  193 + User.where(name: name).first
  194 + end
186 195  
187 196 def filter filter_name
188 197 case filter_name
... ... @@ -250,6 +259,10 @@ class User &lt; ActiveRecord::Base
250 259 end
251 260 end
252 261  
  262 + def unique_email
  263 + self.errors.add(:email, 'has already been taken') if Email.exists?(email: self.email)
  264 + end
  265 +
253 266 # Groups user has access to
254 267 def authorized_groups
255 268 @authorized_groups ||= begin
... ...
app/observers/email_observer.rb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +class EmailObserver < BaseObserver
  2 + def after_create(email)
  3 + notification.new_email(email)
  4 + end
  5 +end
... ...
app/services/git_push_service.rb
... ... @@ -188,8 +188,6 @@ class GitPushService
188 188 end
189 189  
190 190 def commit_user commit
191   - User.where(email: commit.author_email).first ||
192   - User.where(name: commit.author_name).first ||
193   - user
  191 + User.find_for_commit(commit.author_email, commit.author_name) || user
194 192 end
195 193 end
... ...
app/services/notification_service.rb
... ... @@ -17,6 +17,13 @@ class NotificationService
17 17 end
18 18 end
19 19  
  20 + # Always notify user about email added to profile
  21 + def new_email(email)
  22 + if email.user
  23 + mailer.new_email_email(email.id)
  24 + end
  25 + end
  26 +
20 27 # When create an issue we should send next emails:
21 28 #
22 29 # * issue assignee if their notification level is not Disabled
... ...
app/views/layouts/nav/_profile.html.haml
... ... @@ -4,6 +4,10 @@
4 4 %i.icon-home
5 5 = nav_link(controller: :accounts) do
6 6 = link_to "Account", profile_account_path
  7 + = nav_link(controller: :emails) do
  8 + = link_to profile_emails_path do
  9 + Emails
  10 + %span.count= current_user.emails.count + 1
7 11 - unless current_user.ldap_user?
8 12 = nav_link(controller: :passwords) do
9 13 = link_to "Password", edit_profile_password_path
... ...
app/views/notify/new_email_email.html.haml 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +%p
  2 + Hi #{@user.name}!
  3 +%p
  4 + A new email was added to your account:
  5 +%p
  6 + email:
  7 + %code= @email.email
  8 +%p
  9 + If this email was added in error, you can remove it here:
  10 + = link_to "Emails", profile_emails_url
... ...
app/views/notify/new_email_email.text.erb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +Hi <%= @user.name %>!
  2 +
  3 +A new email was added to your account:
  4 +
  5 +email.................. <%= @email.email %>
  6 +
  7 +If this email was added in error, you can remove it here: <%= profile_emails_url %>
... ...
app/views/profiles/emails/index.html.haml 0 → 100644
... ... @@ -0,0 +1,29 @@
  1 +%h3.page-title
  2 + My Email Addresses
  3 +%p.light
  4 + Your
  5 + %b Primary Email
  6 + will be used for account notifications, avatar detection and web based operations, such as edits and merges. All email addresses will be used to identify your commits.
  7 +
  8 +.ui-box
  9 + .title
  10 + Emails (#{@emails.count + 1})
  11 + %ul.well-list#emails-table
  12 + %li
  13 + %strong= @primary
  14 + %span.label.label-success Primary Email
  15 + - @emails.each do |email|
  16 + %li
  17 + %strong= email.email
  18 + %span.cgray
  19 + added #{time_ago_with_tooltip(email.created_at)}
  20 + = link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-small btn-remove pull-right'
  21 +
  22 +%h3.page-title Add Email Address
  23 += form_for 'email', url: profile_emails_path, html: { class: 'form-horizontal' } do |f|
  24 + .form-group
  25 + = f.label :email, class: 'control-label'
  26 + .col-sm-10
  27 + = f.text_field :email, class: 'form-control'
  28 + .form-actions
  29 + = f.submit 'Add', class: 'btn btn-create'
0 30 \ No newline at end of file
... ...
config/routes.rb
... ... @@ -124,6 +124,7 @@ Gitlab::Application.routes.draw do
124 124 end
125 125 end
126 126 resources :keys
  127 + resources :emails, only: [:index, :create, :destroy]
127 128 resources :groups, only: [:index] do
128 129 member do
129 130 delete :leave
... ...
db/migrate/20140209025651_create_emails.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class CreateEmails < ActiveRecord::Migration
  2 + def change
  3 + create_table :emails do |t|
  4 + t.integer :user_id, null: false
  5 + t.string :email, null: false
  6 +
  7 + t.timestamps
  8 + end
  9 +
  10 + add_index :emails, :user_id
  11 + add_index :emails, :email, unique: true
  12 + end
  13 +end
... ...
db/schema.rb
... ... @@ -11,7 +11,7 @@
11 11 #
12 12 # It's strongly recommended that you check this file into your version control system.
13 13  
14   -ActiveRecord::Schema.define(version: 20140127170938) do
  14 +ActiveRecord::Schema.define(version: 20140209025651) do
15 15  
16 16 create_table "broadcast_messages", force: true do |t|
17 17 t.text "message", null: false
... ... @@ -33,6 +33,16 @@ ActiveRecord::Schema.define(version: 20140127170938) do
33 33  
34 34 add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
35 35  
  36 + create_table "emails", force: true do |t|
  37 + t.integer "user_id", null: false
  38 + t.string "email", null: false
  39 + t.datetime "created_at"
  40 + t.datetime "updated_at"
  41 + end
  42 +
  43 + add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree
  44 + add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree
  45 +
36 46 create_table "events", force: true do |t|
37 47 t.string "target_type"
38 48 t.integer "target_id"
... ... @@ -66,8 +76,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
66 76 t.integer "assignee_id"
67 77 t.integer "author_id"
68 78 t.integer "project_id"
69   - t.datetime "created_at"
70   - t.datetime "updated_at"
  79 + t.datetime "created_at", null: false
  80 + t.datetime "updated_at", null: false
71 81 t.integer "position", default: 0
72 82 t.string "branch_name"
73 83 t.text "description"
... ... @@ -85,8 +95,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
85 95  
86 96 create_table "keys", force: true do |t|
87 97 t.integer "user_id"
88   - t.datetime "created_at"
89   - t.datetime "updated_at"
  98 + t.datetime "created_at", null: false
  99 + t.datetime "updated_at", null: false
90 100 t.text "key"
91 101 t.string "title"
92 102 t.string "type"
... ... @@ -111,8 +121,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
111 121 t.integer "author_id"
112 122 t.integer "assignee_id"
113 123 t.string "title"
114   - t.datetime "created_at"
115   - t.datetime "updated_at"
  124 + t.datetime "created_at", null: false
  125 + t.datetime "updated_at", null: false
116 126 t.integer "milestone_id"
117 127 t.string "state"
118 128 t.string "merge_status"
... ... @@ -164,8 +174,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
164 174 t.text "note"
165 175 t.string "noteable_type"
166 176 t.integer "author_id"
167   - t.datetime "created_at"
168   - t.datetime "updated_at"
  177 + t.datetime "created_at", null: false
  178 + t.datetime "updated_at", null: false
169 179 t.integer "project_id"
170 180 t.string "attachment"
171 181 t.string "line_code"
... ... @@ -187,8 +197,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
187 197 t.string "name"
188 198 t.string "path"
189 199 t.text "description"
190   - t.datetime "created_at"
191   - t.datetime "updated_at"
  200 + t.datetime "created_at", null: false
  201 + t.datetime "updated_at", null: false
192 202 t.integer "creator_id"
193 203 t.boolean "issues_enabled", default: true, null: false
194 204 t.boolean "wall_enabled", default: true, null: false
... ... @@ -239,8 +249,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
239 249 t.text "content", limit: 2147483647
240 250 t.integer "author_id", null: false
241 251 t.integer "project_id"
242   - t.datetime "created_at"
243   - t.datetime "updated_at"
  252 + t.datetime "created_at", null: false
  253 + t.datetime "updated_at", null: false
244 254 t.string "file_name"
245 255 t.datetime "expires_at"
246 256 t.boolean "private", default: true, null: false
... ... @@ -262,42 +272,45 @@ ActiveRecord::Schema.define(version: 20140127170938) do
262 272 t.datetime "created_at"
263 273 end
264 274  
  275 + add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
  276 + add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
  277 +
265 278 create_table "tags", force: true do |t|
266 279 t.string "name"
267 280 end
268 281  
269 282 create_table "users", force: true do |t|
270   - t.string "email", default: "", null: false
271   - t.string "encrypted_password", limit: 128, default: "", null: false
  283 + t.string "email", default: "", null: false
  284 + t.string "encrypted_password", default: "", null: false
272 285 t.string "reset_password_token"
273 286 t.datetime "reset_password_sent_at"
274 287 t.datetime "remember_created_at"
275   - t.integer "sign_in_count", default: 0
  288 + t.integer "sign_in_count", default: 0
276 289 t.datetime "current_sign_in_at"
277 290 t.datetime "last_sign_in_at"
278 291 t.string "current_sign_in_ip"
279 292 t.string "last_sign_in_ip"
280   - t.datetime "created_at"
281   - t.datetime "updated_at"
  293 + t.datetime "created_at", null: false
  294 + t.datetime "updated_at", null: false
282 295 t.string "name"
283   - t.boolean "admin", default: false, null: false
284   - t.integer "projects_limit", default: 10
285   - t.string "skype", default: "", null: false
286   - t.string "linkedin", default: "", null: false
287   - t.string "twitter", default: "", null: false
  296 + t.boolean "admin", default: false, null: false
  297 + t.integer "projects_limit", default: 10
  298 + t.string "skype", default: "", null: false
  299 + t.string "linkedin", default: "", null: false
  300 + t.string "twitter", default: "", null: false
288 301 t.string "authentication_token"
289   - t.integer "theme_id", default: 1, null: false
  302 + t.integer "theme_id", default: 1, null: false
290 303 t.string "bio"
291   - t.integer "failed_attempts", default: 0
  304 + t.integer "failed_attempts", default: 0
292 305 t.datetime "locked_at"
293 306 t.string "extern_uid"
294 307 t.string "provider"
295 308 t.string "username"
296   - t.boolean "can_create_group", default: true, null: false
297   - t.boolean "can_create_team", default: true, null: false
  309 + t.boolean "can_create_group", default: true, null: false
  310 + t.boolean "can_create_team", default: true, null: false
298 311 t.string "state"
299   - t.integer "color_scheme_id", default: 1, null: false
300   - t.integer "notification_level", default: 1, null: false
  312 + t.integer "color_scheme_id", default: 1, null: false
  313 + t.integer "notification_level", default: 1, null: false
301 314 t.datetime "password_expires_at"
302 315 t.integer "created_by_id"
303 316 t.string "avatar"
... ... @@ -305,14 +318,15 @@ ActiveRecord::Schema.define(version: 20140127170938) do
305 318 t.datetime "confirmed_at"
306 319 t.datetime "confirmation_sent_at"
307 320 t.string "unconfirmed_email"
308   - t.boolean "hide_no_ssh_key", default: false
309   - t.string "website_url", default: "", null: false
  321 + t.boolean "hide_no_ssh_key", default: false
  322 + t.string "website_url", default: "", null: false
310 323 end
311 324  
312 325 add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
313 326 add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
314 327 add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
315 328 add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
  329 + add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree
316 330 add_index "users", ["name"], name: "index_users_on_name", using: :btree
317 331 add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
318 332 add_index "users", ["username"], name: "index_users_on_username", using: :btree
... ... @@ -331,8 +345,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
331 345 create_table "users_projects", force: true do |t|
332 346 t.integer "user_id", null: false
333 347 t.integer "project_id", null: false
334   - t.datetime "created_at"
335   - t.datetime "updated_at"
  348 + t.datetime "created_at", null: false
  349 + t.datetime "updated_at", null: false
336 350 t.integer "project_access", default: 0, null: false
337 351 t.integer "notification_level", default: 3, null: false
338 352 end
... ... @@ -344,8 +358,8 @@ ActiveRecord::Schema.define(version: 20140127170938) do
344 358 create_table "web_hooks", force: true do |t|
345 359 t.string "url"
346 360 t.integer "project_id"
347   - t.datetime "created_at"
348   - t.datetime "updated_at"
  361 + t.datetime "created_at", null: false
  362 + t.datetime "updated_at", null: false
349 363 t.string "type", default: "ProjectHook"
350 364 t.integer "service_id"
351 365 t.boolean "push_events", default: true, null: false
... ...
features/profile/emails.feature 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +Feature: Profile Emails
  2 + Background:
  3 + Given I sign in as a user
  4 + And I visit profile emails page
  5 +
  6 + Scenario: I should see emails
  7 + Then I should see my emails
  8 +
  9 + Scenario: Add new email
  10 + Given I submit new email "my@email.com"
  11 + Then I should see new email "my@email.com"
  12 + And I should see my emails
  13 +
  14 + Scenario: Add duplicate email
  15 + Given I submit duplicate email @user.email
  16 + Then I should not have @user.email added
  17 + And I should see my emails
  18 +
  19 + Scenario: Remove email
  20 + Given I submit new email "my@email.com"
  21 + Then I should see new email "my@email.com"
  22 + And I should see my emails
  23 + Then I click link "Remove" for "my@email.com"
  24 + Then I should not see email "my@email.com"
  25 + And I should see my emails
... ...
features/project/commits/commits_user_lookup.feature 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +Feature: Project Browse Commits User Lookup
  2 + Background:
  3 + Given I sign in as a user
  4 + And I own a project
  5 + And I have the user that authored the commits
  6 + And I visit my project's commits page
  7 +
  8 + Scenario: I browse commit from list
  9 + Given I click on commit link
  10 + Then I see commit info
  11 +
  12 + Scenario: I browse another commit from list
  13 + Given I click on another commit link
  14 + Then I see other commit info
0 15 \ No newline at end of file
... ...
features/steps/profile/profile_emails.rb 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +class ProfileEmails < Spinach::FeatureSteps
  2 + include SharedAuthentication
  3 +
  4 + Then 'I visit profile emails page' do
  5 + visit profile_emails_path
  6 + end
  7 +
  8 + Then 'I should see my emails' do
  9 + page.should have_content(@user.email)
  10 + @user.emails.each do |email|
  11 + page.should have_content(email.email)
  12 + end
  13 + end
  14 +
  15 + And 'I submit new email "my@email.com"' do
  16 + fill_in "email_email", with: "my@email.com"
  17 + click_button "Add"
  18 + end
  19 +
  20 + Then 'I should see new email "my@email.com"' do
  21 + email = @user.emails.find_by(email: "my@email.com")
  22 + email.should_not be_nil
  23 + page.should have_content("my@email.com")
  24 + end
  25 +
  26 + Then 'I should not see email "my@email.com"' do
  27 + email = @user.emails.find_by(email: "my@email.com")
  28 + email.should be_nil
  29 + page.should_not have_content("my@email.com")
  30 + end
  31 +
  32 + Then 'I click link "Remove" for "my@email.com"' do
  33 + # there should only be one remove button at this time
  34 + click_link "Remove"
  35 + # force these to reload as they have been cached
  36 + @user.emails.reload
  37 + end
  38 +
  39 + And 'I submit duplicate email @user.email' do
  40 + fill_in "email_email", with: @user.email
  41 + click_button "Add"
  42 + end
  43 +
  44 + Then 'I should not have @user.email added' do
  45 + email = @user.emails.find_by(email: @user.email)
  46 + email.should be_nil
  47 + end
  48 +end
... ...
features/steps/project/project_browse_commits_user_lookup.rb 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +class ProjectBrowseCommitsUserLookup < Spinach::FeatureSteps
  2 + include SharedAuthentication
  3 + include SharedProject
  4 + include SharedPaths
  5 +
  6 + Given 'I have the user that authored the commits' do
  7 + @user = create(:user, email: 'dmitriy.zaporozhets@gmail.com')
  8 + create(:email, { user: @user, email: 'dzaporozhets@sphereconsultinginc.com' })
  9 + end
  10 +
  11 + Given 'I click on commit link' do
  12 + visit project_commit_path(@project, ValidCommit::ID)
  13 + end
  14 +
  15 + Given 'I click on another commit link' do
  16 + visit project_commit_path(@project, ValidCommitWithAltEmail::ID)
  17 + end
  18 +
  19 + Then 'I see commit info' do
  20 + page.should have_content ValidCommit::MESSAGE
  21 + check_author_link(ValidCommit::AUTHOR_EMAIL)
  22 + end
  23 +
  24 + Then 'I see other commit info' do
  25 + page.should have_content ValidCommitWithAltEmail::MESSAGE
  26 + check_author_link(ValidCommitWithAltEmail::AUTHOR_EMAIL)
  27 + end
  28 +
  29 + def check_author_link(email)
  30 + author_link = find('.commit-author-link')
  31 + author_link['href'].should == user_path(@user)
  32 + author_link['data-original-title'].should == email
  33 + find('.commit-author-name').text.should == @user.name
  34 + end
  35 +end
... ...
features/support/env.rb
... ... @@ -15,7 +15,7 @@ require &#39;spinach/capybara&#39;
15 15 require 'sidekiq/testing/inline'
16 16  
17 17  
18   -%w(valid_commit big_commits select2_helper test_env).each do |f|
  18 +%w(valid_commit valid_commit_with_alt_email big_commits select2_helper test_env).each do |f|
19 19 require Rails.root.join('spec', 'support', f)
20 20 end
21 21  
... ...
spec/factories.rb
... ... @@ -219,6 +219,19 @@ FactoryGirl.define do
219 219 end
220 220 end
221 221 end
  222 +
  223 + factory :email do
  224 + user
  225 + email do
  226 + Faker::Internet.email('alias')
  227 + end
  228 +
  229 + factory :another_email do
  230 + email do
  231 + Faker::Internet.email('another.alias')
  232 + end
  233 + end
  234 + end
222 235  
223 236 factory :milestone do
224 237 title
... ...
spec/mailers/notify_spec.rb
... ... @@ -90,6 +90,28 @@ describe Notify do
90 90 end
91 91 end
92 92  
  93 + describe 'user added email' do
  94 + let(:email) { create(:email) }
  95 +
  96 + subject { Notify.new_email_email(email.id) }
  97 +
  98 + it 'is sent to the new user' do
  99 + should deliver_to email.user.email
  100 + end
  101 +
  102 + it 'has the correct subject' do
  103 + should have_subject /^gitlab \| Email was added to your account$/i
  104 + end
  105 +
  106 + it 'contains the new email address' do
  107 + should have_body_text /#{email.email}/
  108 + end
  109 +
  110 + it 'includes a link to emails page' do
  111 + should have_body_text /#{profile_emails_path}/
  112 + end
  113 + end
  114 +
93 115 context 'for a project' do
94 116 describe 'items that are assignable, the email' do
95 117 let(:assignee) { create(:user, email: 'assignee@example.com') }
... ...
spec/observers/email_observer_spec.rb 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +require 'spec_helper'
  2 +
  3 +describe EmailObserver do
  4 + let(:email) { create(:email) }
  5 +
  6 + before { subject.stub(notification: double('NotificationService').as_null_object) }
  7 +
  8 + subject { EmailObserver.instance }
  9 +
  10 + describe '#after_create' do
  11 + it 'trigger notification to send emails' do
  12 + subject.should_receive(:notification)
  13 +
  14 + subject.after_create(email)
  15 + end
  16 + end
  17 +end
... ...
spec/routing/routing_spec.rb
... ... @@ -183,6 +183,23 @@ describe Profiles::KeysController, &quot;routing&quot; do
183 183 end
184 184 end
185 185  
  186 +# emails GET /emails(.:format) emails#index
  187 +# POST /keys(.:format) emails#create
  188 +# DELETE /keys/:id(.:format) keys#destroy
  189 +describe Profiles::EmailsController, "routing" do
  190 + it "to #index" do
  191 + get("/profile/emails").should route_to('profiles/emails#index')
  192 + end
  193 +
  194 + it "to #create" do
  195 + post("/profile/emails").should route_to('profiles/emails#create')
  196 + end
  197 +
  198 + it "to #destroy" do
  199 + delete("/profile/emails/1").should route_to('profiles/emails#destroy', id: '1')
  200 + end
  201 +end
  202 +
186 203 # profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy
187 204 describe Profiles::AvatarsController, "routing" do
188 205 it "to #destroy" do
... ...
spec/services/notification_service_spec.rb
... ... @@ -16,6 +16,19 @@ describe NotificationService do
16 16 end
17 17 end
18 18  
  19 + describe 'Email' do
  20 + describe :new_email do
  21 + let(:email) { create(:email) }
  22 +
  23 + it { notification.new_email(email).should be_true }
  24 +
  25 + it 'should send email to email owner' do
  26 + Notify.should_receive(:new_email_email).with(email.id)
  27 + notification.new_email(email)
  28 + end
  29 + end
  30 + end
  31 +
19 32 describe 'Notes' do
20 33 context 'issue note' do
21 34 let(:issue) { create(:issue, assignee: create(:user)) }
... ...
spec/support/valid_commit.rb
... ... @@ -2,6 +2,7 @@ module ValidCommit
2 2 ID = "8470d70da67355c9c009e4401746b1d5410af2e3"
3 3 MESSAGE = "notes controller refactored"
4 4 AUTHOR_FULL_NAME = "Dmitriy Zaporozhets"
  5 + AUTHOR_EMAIL = "dmitriy.zaporozhets@gmail.com"
5 6  
6 7 FILES = [".foreman", ".gitignore", ".rails_footnotes", ".rspec", ".travis.yml", "CHANGELOG", "Gemfile", "Gemfile.lock", "LICENSE", "Procfile", "Procfile.production", "README.md", "Rakefile", "VERSION", "app", "config.ru", "config", "db", "doc", "lib", "log", "public", "resque.sh", "script", "spec", "vendor"]
7 8 FILES_COUNT = 26
... ...
spec/support/valid_commit_with_alt_email.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +module ValidCommitWithAltEmail
  2 + ID = "1e689bfba39525ead225eaf611948cfbe8ac34cf"
  3 + MESSAGE = "fixed notes logic"
  4 + AUTHOR_FULL_NAME = "Dmitriy Zaporozhets"
  5 + AUTHOR_EMAIL = "dzaporozhets@sphereconsultinginc.com"
  6 +end
0 7 \ No newline at end of file
... ...