BigW Consortium Gitlab

notify_spec.rb 46.5 KB
Newer Older
1
require 'spec_helper'
2
require 'email_spec'
3 4 5 6

describe Notify do
  include EmailSpec::Helpers
  include EmailSpec::Matchers
Dmitriy Zaporozhets committed
7
  include RepoHelpers
8

9
  include_context 'gitlab email notification'
10

11 12
  context 'for a project' do
    describe 'items that are assignable, the email' do
13
      let(:current_user) { create(:user, email: "current@email.com") }
14
      let(:assignee) { create(:user, email: 'assignee@example.com', name: 'John Doe') }
15
      let(:previous_assignee) { create(:user, name: 'Previous Assignee') }
16

17
      shared_examples 'an assignee email' do
18 19
        it 'is sent as the author' do
          sender = subject.header[:from].addrs[0]
20 21
          expect(sender.display_name).to eq(current_user.name)
          expect(sender.address).to eq(gitlab_sender)
22 23
        end

24
        it 'is sent to the assignee' do
25
          is_expected.to deliver_to assignee.email
26 27
        end
      end
28

29
      context 'for issues' do
30
        let(:issue) { create(:issue, author: current_user, assignee: assignee, project: project) }
Robert Speicher committed
31
        let(:issue_with_description) { create(:issue, author: current_user, assignee: assignee, project: project, description: FFaker::Lorem.sentence) }
32

33
        describe 'that are new' do
34
          subject { Notify.new_issue_email(issue.assignee_id, issue.id) }
35

36
          it_behaves_like 'an assignee email'
37 38 39
          it_behaves_like 'an email starting a new thread with reply-by-email enabled' do
            let(:model) { issue }
          end
40
          it_behaves_like 'it should show Gmail Actions View Issue link'
41
          it_behaves_like 'an unsubscribeable thread'
42

43
          it 'has the correct subject' do
44
            is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/
45
          end
46

47
          it 'contains a link to the new issue' do
Vinnie Okada committed
48
            is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
49
          end
50 51 52

          context 'when enabled email_author_in_body' do
            before do
Sean McGivern committed
53
              stub_application_setting(email_author_in_body: true)
54 55 56
            end

            it 'contains a link to note author' do
57
              is_expected.to have_html_escaped_body_text issue.author_name
58 59 60
              is_expected.to have_body_text /wrote\:/
            end
          end
61
        end
62

63 64 65
        describe 'that are new with a description' do
          subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) }

66 67
          it_behaves_like 'it should show Gmail Actions View Issue link'

68
          it 'contains the description' do
69
            is_expected.to have_html_escaped_body_text issue_with_description.description
70 71 72
          end
        end

73
        describe 'that have been reassigned' do
74
          subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) }
75 76

          it_behaves_like 'a multiple recipients email'
77 78 79
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
80
          it_behaves_like 'it should show Gmail Actions View Issue link'
81
          it_behaves_like 'an unsubscribeable thread'
82

83 84
          it 'is sent as the author' do
            sender = subject.header[:from].addrs[0]
85 86
            expect(sender.display_name).to eq(current_user.name)
            expect(sender.address).to eq(gitlab_sender)
87 88
          end

89
          it 'has the correct subject' do
90
            is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/
91 92 93
          end

          it 'contains the name of the previous assignee' do
94
            is_expected.to have_html_escaped_body_text previous_assignee.name
95 96 97
          end

          it 'contains the name of the new assignee' do
98
            is_expected.to have_html_escaped_body_text assignee.name
99 100 101
          end

          it 'contains a link to the issue' do
Vinnie Okada committed
102
            is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
103 104
          end
        end
105

106 107 108 109
        describe 'that have been relabeled' do
          subject { Notify.relabeled_issue_email(recipient.id, issue.id, %w[foo bar baz], current_user.id) }

          it_behaves_like 'a multiple recipients email'
110 111 112
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
          it_behaves_like 'it should show Gmail Actions View Issue link'
          it_behaves_like 'a user cannot unsubscribe through footer link'
          it_behaves_like 'an email with a labels subscriptions link in its footer'

          it 'is sent as the author' do
            sender = subject.header[:from].addrs[0]
            expect(sender.display_name).to eq(current_user.name)
            expect(sender.address).to eq(gitlab_sender)
          end

          it 'has the correct subject' do
            is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/
          end

          it 'contains the names of the added labels' do
            is_expected.to have_body_text /foo, bar, and baz/
          end

          it 'contains a link to the issue' do
            is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
          end
        end

136 137
        describe 'status changed' do
          let(:status) { 'closed' }
138
          subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) }
139

140 141 142
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
143
          it_behaves_like 'it should show Gmail Actions View Issue link'
144
          it_behaves_like 'an unsubscribeable thread'
145

146 147
          it 'is sent as the author' do
            sender = subject.header[:from].addrs[0]
148 149
            expect(sender.display_name).to eq(current_user.name)
            expect(sender.address).to eq(gitlab_sender)
150 151
          end

152
          it 'has the correct subject' do
153
            is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/i
154 155 156
          end

          it 'contains the new status' do
157
            is_expected.to have_body_text /#{status}/i
158 159 160
          end

          it 'contains the user name' do
161
            is_expected.to have_html_escaped_body_text current_user.name
162 163 164
          end

          it 'contains a link to the issue' do
Vinnie Okada committed
165
            is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
166
          end
167
        end
168 169 170 171 172

        describe 'moved to another project' do
          let(:new_issue) { create(:issue) }
          subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) }

173 174 175
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
          it_behaves_like 'it should show Gmail Actions View Issue link'
          it_behaves_like 'an unsubscribeable thread'

          it 'contains description about action taken' do
            is_expected.to have_body_text 'Issue was moved to another project'
          end

          it 'has the correct subject' do
            is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/i
          end

          it 'contains link to new issue' do
            new_issue_url = namespace_project_issue_path(new_issue.project.namespace,
                                                         new_issue.project, new_issue)
            is_expected.to have_body_text new_issue_url
          end

          it 'contains a link to the original issue' do
            is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
          end
        end
197 198 199
      end

      context 'for merge requests' do
200
        let(:merge_author) { create(:user) }
201
        let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
Robert Speicher committed
202
        let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: FFaker::Lorem.sentence) }
203 204

        describe 'that are new' do
205
          subject { Notify.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
206 207

          it_behaves_like 'an assignee email'
208 209 210
          it_behaves_like 'an email starting a new thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
211
          it_behaves_like 'it should show Gmail Actions View Merge request link'
212
          it_behaves_like 'an unsubscribeable thread'
213 214

          it 'has the correct subject' do
215
            is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
216 217 218
          end

          it 'contains a link to the new merge request' do
Vinnie Okada committed
219
            is_expected.to have_body_text /#{namespace_project_merge_request_path(project.namespace, project, merge_request)}/
220 221 222
          end

          it 'contains the source branch for the merge request' do
223
            is_expected.to have_body_text /#{merge_request.source_branch}/
224 225 226
          end

          it 'contains the target branch for the merge request' do
227
            is_expected.to have_body_text /#{merge_request.target_branch}/
228
          end
Philip Blatter committed
229

230 231
          context 'when enabled email_author_in_body' do
            before do
Sean McGivern committed
232
              stub_application_setting(email_author_in_body: true)
233 234 235
            end

            it 'contains a link to note author' do
236
              is_expected.to have_html_escaped_body_text merge_request.author_name
237 238 239
              is_expected.to have_body_text /wrote\:/
            end
          end
240 241
        end

242 243 244
        describe 'that are new with a description' do
          subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) }

245
          it_behaves_like 'it should show Gmail Actions View Merge request link'
246
          it_behaves_like "an unsubscribeable thread"
247

248
          it 'contains the description' do
249
            is_expected.to have_html_escaped_body_text merge_request_with_description.description
250 251 252
          end
        end

253
        describe 'that are reassigned' do
254
          subject { Notify.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
255 256

          it_behaves_like 'a multiple recipients email'
257 258 259
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
260
          it_behaves_like 'it should show Gmail Actions View Merge request link'
261
          it_behaves_like "an unsubscribeable thread"
262

263 264
          it 'is sent as the author' do
            sender = subject.header[:from].addrs[0]
265 266
            expect(sender.display_name).to eq(current_user.name)
            expect(sender.address).to eq(gitlab_sender)
267 268
          end

269
          it 'has the correct subject' do
270
            is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
271 272 273
          end

          it 'contains the name of the previous assignee' do
274
            is_expected.to have_html_escaped_body_text previous_assignee.name
275 276 277
          end

          it 'contains the name of the new assignee' do
278
            is_expected.to have_html_escaped_body_text assignee.name
279 280 281
          end

          it 'contains a link to the merge request' do
Vinnie Okada committed
282
            is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/
283
          end
284 285
        end

286 287 288 289
        describe 'that have been relabeled' do
          subject { Notify.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) }

          it_behaves_like 'a multiple recipients email'
290 291 292
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
293 294 295 296 297 298 299 300 301 302 303
          it_behaves_like 'it should show Gmail Actions View Merge request link'
          it_behaves_like 'a user cannot unsubscribe through footer link'
          it_behaves_like 'an email with a labels subscriptions link in its footer'

          it 'is sent as the author' do
            sender = subject.header[:from].addrs[0]
            expect(sender.display_name).to eq(current_user.name)
            expect(sender.address).to eq(gitlab_sender)
          end

          it 'has the correct subject' do
304
            is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
305 306 307 308 309 310 311 312 313 314 315
          end

          it 'contains the names of the added labels' do
            is_expected.to have_body_text /foo, bar, and baz/
          end

          it 'contains a link to the merge request' do
            is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/
          end
        end

316 317
        describe 'status changed' do
          let(:status) { 'reopened' }
318
          subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) }
319

320 321 322
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
323
          it_behaves_like 'it should show Gmail Actions View Merge request link'
324
          it_behaves_like 'an unsubscribeable thread'
325 326 327

          it 'is sent as the author' do
            sender = subject.header[:from].addrs[0]
328 329
            expect(sender.display_name).to eq(current_user.name)
            expect(sender.address).to eq(gitlab_sender)
330 331 332
          end

          it 'has the correct subject' do
333
            is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/i
334 335 336
          end

          it 'contains the new status' do
337
            is_expected.to have_body_text /#{status}/i
338 339 340
          end

          it 'contains the user name' do
341
            is_expected.to have_html_escaped_body_text current_user.name
342 343 344
          end

          it 'contains a link to the merge request' do
Vinnie Okada committed
345
            is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/
346 347 348
          end
        end

349 350 351 352
        describe 'that are merged' do
          subject { Notify.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) }

          it_behaves_like 'a multiple recipients email'
353 354 355
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
356
          it_behaves_like 'it should show Gmail Actions View Merge request link'
357
          it_behaves_like 'an unsubscribeable thread'
358 359 360

          it 'is sent as the merge author' do
            sender = subject.header[:from].addrs[0]
361 362
            expect(sender.display_name).to eq(merge_author.name)
            expect(sender.address).to eq(gitlab_sender)
363 364 365
          end

          it 'has the correct subject' do
366
            is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
367
          end
368

369
          it 'contains the new status' do
370
            is_expected.to have_body_text /merged/i
371 372 373
          end

          it 'contains a link to the merge request' do
Vinnie Okada committed
374
            is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/
375
          end
376 377
        end
      end
378 379
    end

380 381 382
    describe 'project was moved' do
      let(:project) { create(:project) }
      let(:user) { create(:user) }
383
      subject { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") }
384

385
      it_behaves_like 'an email sent from GitLab'
386
      it_behaves_like 'it should not have Gmail Actions links'
387
      it_behaves_like "a user cannot unsubscribe through footer link"
388

389
      it 'has the correct subject' do
390
        is_expected.to have_subject /Project was moved/
391 392 393
      end

      it 'contains name of project' do
394
        is_expected.to have_html_escaped_body_text project.name_with_namespace
395 396 397
      end

      it 'contains new user role' do
398
        is_expected.to have_body_text /#{project.ssh_url_to_repo}/
399 400 401
      end
    end

402
    describe 'project access requested' do
403
      context 'for a project in a user namespace' do
404 405 406 407 408 409
        let(:project) do
          create(:empty_project, :public, :access_requestable) do |project|
            project.team << [project.owner, :master, project.owner]
          end
        end

410 411 412
        let(:user) { create(:user) }
        let(:project_member) do
          project.request_access(user)
413
          project.requesters.find_by(user_id: user.id)
414 415 416 417 418 419 420 421 422 423 424 425 426
        end
        subject { Notify.member_access_requested_email('project', project_member.id) }

        it_behaves_like 'an email sent from GitLab'
        it_behaves_like 'it should not have Gmail Actions links'
        it_behaves_like "a user cannot unsubscribe through footer link"

        it 'contains all the useful information' do
          to_emails = subject.header[:to].addrs
          expect(to_emails.size).to eq(1)
          expect(to_emails[0].address).to eq(project.members.owners_and_masters.first.user.notification_email)

          is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
427 428 429
          is_expected.to have_html_escaped_body_text project.name_with_namespace
          is_expected.to have_body_text namespace_project_project_members_url(project.namespace, project)
          is_expected.to have_body_text project_member.human_access
430
        end
431 432
      end

433 434 435
      context 'for a project in a group' do
        let(:group_owner) { create(:user) }
        let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } }
436
        let(:project) { create(:empty_project, :public, :access_requestable, namespace: group) }
437 438 439
        let(:user) { create(:user) }
        let(:project_member) do
          project.request_access(user)
440
          project.requesters.find_by(user_id: user.id)
441 442
        end
        subject { Notify.member_access_requested_email('project', project_member.id) }
443

444 445 446 447 448 449 450 451 452 453
        it_behaves_like 'an email sent from GitLab'
        it_behaves_like 'it should not have Gmail Actions links'
        it_behaves_like "a user cannot unsubscribe through footer link"

        it 'contains all the useful information' do
          to_emails = subject.header[:to].addrs
          expect(to_emails.size).to eq(1)
          expect(to_emails[0].address).to eq(group.members.owners_and_masters.first.user.notification_email)

          is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
454 455 456
          is_expected.to have_html_escaped_body_text project.name_with_namespace
          is_expected.to have_body_text namespace_project_project_members_url(project.namespace, project)
          is_expected.to have_body_text project_member.human_access
457
        end
458
      end
459 460 461
    end

    describe 'project access denied' do
462
      let(:project) { create(:empty_project, :public, :access_requestable) }
463 464 465
      let(:user) { create(:user) }
      let(:project_member) do
        project.request_access(user)
466
        project.requesters.find_by(user_id: user.id)
467
      end
468
      subject { Notify.member_access_denied_email('project', project.id, user.id) }
469 470 471 472 473

      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"

474 475
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was denied"
476 477
        is_expected.to have_html_escaped_body_text project.name_with_namespace
        is_expected.to have_body_text project.web_url
478
      end
479 480
    end

481
    describe 'project access changed' do
482 483
      let(:owner) { create(:user, name: "Chang O'Keefe") }
      let(:project) { create(:empty_project, :public, :access_requestable, namespace: owner.namespace) }
484
      let(:user) { create(:user) }
485
      let(:project_member) { create(:project_member, project: project, user: user) }
486
      subject { Notify.member_access_granted_email('project', project_member.id) }
487 488

      it_behaves_like 'an email sent from GitLab'
489
      it_behaves_like 'it should not have Gmail Actions links'
490
      it_behaves_like "a user cannot unsubscribe through footer link"
491

492 493
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was granted"
494 495 496
        is_expected.to have_html_escaped_body_text project.name_with_namespace
        is_expected.to have_body_text project.web_url
        is_expected.to have_body_text project_member.human_access
497
      end
498
    end
499

500 501 502 503 504 505 506 507 508
    def invite_to_project(project, inviter:)
      create(
        :project_member,
        :developer,
        project: project,
        invite_token: '1234',
        invite_email: 'toto@example.com',
        user: nil,
        created_by: inviter
509
      )
510 511 512 513 514
    end

    describe 'project invitation' do
      let(:project) { create(:project) }
      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
515
      let(:project_member) { invite_to_project(project, inviter: master) }
516 517 518 519 520 521 522

      subject { Notify.member_invited_email('project', project_member.id, project_member.invite_token) }

      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"

523 524
      it 'contains all the useful information' do
        is_expected.to have_subject "Invitation to join the #{project.name_with_namespace} project"
525 526 527 528
        is_expected.to have_html_escaped_body_text project.name_with_namespace
        is_expected.to have_body_text project.web_url
        is_expected.to have_body_text project_member.human_access
        is_expected.to have_body_text project_member.invite_token
529
      end
530 531 532 533
    end

    describe 'project invitation accepted' do
      let(:project) { create(:project) }
534
      let(:invited_user) { create(:user, name: 'invited user') }
535 536
      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
      let(:project_member) do
537
        invitee = invite_to_project(project, inviter: master)
538 539
        invitee.accept_invite!(invited_user)
        invitee
540
      end
541

542 543 544 545 546 547
      subject { Notify.member_invite_accepted_email('project', project_member.id) }

      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"

548 549
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation accepted'
550 551 552 553
        is_expected.to have_html_escaped_body_text project.name_with_namespace
        is_expected.to have_body_text project.web_url
        is_expected.to have_body_text project_member.invite_email
        is_expected.to have_html_escaped_body_text invited_user.name
554
      end
555 556 557 558 559 560
    end

    describe 'project invitation declined' do
      let(:project) { create(:project) }
      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
      let(:project_member) do
561
        invitee = invite_to_project(project, inviter: master)
562 563
        invitee.decline_invite!
        invitee
564
      end
565 566 567 568 569 570 571

      subject { Notify.member_invite_declined_email('project', project.id, project_member.invite_email, master.id) }

      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"

572 573
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation declined'
574 575 576
        is_expected.to have_html_escaped_body_text project.name_with_namespace
        is_expected.to have_body_text project.web_url
        is_expected.to have_body_text project_member.invite_email
577
      end
578 579
    end

580
    context 'items that are noteable, the email for a note' do
581 582
      let(:note_author) { create(:user, name: 'author_name') }
      let(:note) { create(:note, project: project, author: note_author) }
583

584 585
      before :each do
        allow(Note).to receive(:find).with(note.id).and_return(note)
586 587
      end

588
      shared_examples 'a note email' do
589 590
        it_behaves_like 'it should have Gmail Actions links'

591 592
        it 'is sent as the author' do
          sender = subject.header[:from].addrs[0]
593 594
          expect(sender.display_name).to eq(note_author.name)
          expect(sender.address).to eq(gitlab_sender)
595 596
        end

597
        it 'is sent to the given recipient' do
598
          is_expected.to deliver_to recipient.notification_email
599 600 601
        end

        it 'contains the message from the note' do
602
          is_expected.to have_html_escaped_body_text note.note
603
        end
604

605
        it 'does not contain note author' do
606 607 608 609 610
          is_expected.not_to have_body_text /wrote\:/
        end

        context 'when enabled email_author_in_body' do
          before do
Sean McGivern committed
611
            stub_application_setting(email_author_in_body: true)
612 613 614
          end

          it 'contains a link to note author' do
615
            is_expected.to have_html_escaped_body_text note.author_name
616 617 618
            is_expected.to have_body_text /wrote\:/
          end
        end
619 620 621
      end

      describe 'on a commit' do
622
        let(:commit) { project.commit }
Dmitriy Zaporozhets committed
623

624
        before(:each) { allow(note).to receive(:noteable).and_return(commit) }
625

Dmitriy Zaporozhets committed
626
        subject { Notify.note_commit_email(recipient.id, note.id) }
627 628

        it_behaves_like 'a note email'
629 630 631
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { commit }
        end
632
        it_behaves_like 'it should show Gmail Actions View Commit link'
633
        it_behaves_like 'a user cannot unsubscribe through footer link'
634 635

        it 'has the correct subject' do
636
          is_expected.to have_subject /Re: #{project.name} | #{commit.title} \(#{commit.short_id}\)/
637 638 639
        end

        it 'contains a link to the commit' do
640
          is_expected.to have_body_text commit.short_id
641 642 643 644
        end
      end

      describe 'on a merge request' do
645
        let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
Vinnie Okada committed
646
        let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") }
647
        before(:each) { allow(note).to receive(:noteable).and_return(merge_request) }
648

649
        subject { Notify.note_merge_request_email(recipient.id, note.id) }
Sean McGivern committed
650

651
        it_behaves_like 'a note email'
652 653 654
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { merge_request }
        end
655
        it_behaves_like 'it should show Gmail Actions View Merge request link'
656
        it_behaves_like 'an unsubscribeable thread'
657 658

        it 'has the correct subject' do
659
          is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/
660 661 662
        end

        it 'contains a link to the merge request note' do
663
          is_expected.to have_body_text /#{note_on_merge_request_path}/
664 665 666 667
        end
      end

      describe 'on an issue' do
668
        let(:issue) { create(:issue, project: project) }
Vinnie Okada committed
669
        let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") }
670
        before(:each) { allow(note).to receive(:noteable).and_return(issue) }
671 672

        subject { Notify.note_issue_email(recipient.id, note.id) }
673 674

        it_behaves_like 'a note email'
675 676 677
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { issue }
        end
678
        it_behaves_like 'it should show Gmail Actions View Issue link'
679
        it_behaves_like 'an unsubscribeable thread'
680 681

        it 'has the correct subject' do
682
          is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/
683 684 685
        end

        it 'contains a link to the issue note' do
686
          is_expected.to have_body_text /#{note_on_issue_path}/
687 688
        end
      end
689
    end
690 691 692 693 694 695 696 697

    context 'items that are noteable, emails for a note on a diff' do
      let(:note_author) { create(:user, name: 'author_name') }

      before :each do
        allow(Note).to receive(:find).with(note.id).and_return(note)
      end

hhoopes committed
698
      shared_examples 'a note email on a diff' do  |model|
699 700 701
        let(:note) { create(model, project: project, author: note_author) }

        it "includes diffs with character-level highlighting" do
702
          is_expected.to have_body_text /<span class=\"p\">}<\/span><\/span>/
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
        end

        it 'contains a link to the diff file' do
          is_expected.to have_body_text /#{note.diff_file.file_path}/
        end

        it_behaves_like 'it should have Gmail Actions links'

        it 'is sent as the author' do
          sender = subject.header[:from].addrs[0]
          expect(sender.display_name).to eq(note_author.name)
          expect(sender.address).to eq(gitlab_sender)
        end

        it 'is sent to the given recipient' do
          is_expected.to deliver_to recipient.notification_email
        end

        it 'contains the message from the note' do
722
          is_expected.to have_html_escaped_body_text note.note
723 724 725 726 727 728 729 730
        end

        it 'does not contain note author' do
          is_expected.not_to have_body_text /wrote\:/
        end

        context 'when enabled email_author_in_body' do
          before do
Sean McGivern committed
731
            stub_application_setting(email_author_in_body: true)
732 733 734
          end

          it 'contains a link to note author' do
735
            is_expected.to have_html_escaped_body_text note.author_name
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
            is_expected.to have_body_text /wrote\:/
          end
        end
      end

      describe 'on a commit' do
        let(:commit) { project.commit }
        let(:note) { create(:diff_note_on_commit) }

        subject { Notify.note_commit_email(recipient.id, note.id) }

        it_behaves_like 'a note email on a diff', :diff_note_on_commit
        it_behaves_like 'it should show Gmail Actions View Commit link'
        it_behaves_like 'a user cannot unsubscribe through footer link'
      end

      describe 'on a merge request' do
        let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
        let(:note) { create(:diff_note_on_merge_request) }

        subject { Notify.note_merge_request_email(recipient.id, note.id) }

        it_behaves_like 'a note email on a diff', :diff_note_on_merge_request
        it_behaves_like 'it should show Gmail Actions View Merge request link'
        it_behaves_like 'an unsubscribeable thread'
      end
    end
763
  end
764

765 766
  context 'for a group' do
    describe 'group access requested' do
767
      let(:group) { create(:group, :public, :access_requestable) }
768 769 770
      let(:user) { create(:user) }
      let(:group_member) do
        group.request_access(user)
771
        group.requesters.find_by(user_id: user.id)
772 773
      end
      subject { Notify.member_access_requested_email('group', group_member.id) }
774

775 776 777
      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"
778

779 780
      it 'contains all the useful information' do
        is_expected.to have_subject "Request to join the #{group.name} group"
781 782 783
        is_expected.to have_html_escaped_body_text group.name
        is_expected.to have_body_text group_group_members_url(group)
        is_expected.to have_body_text group_member.human_access
784
      end
785 786
    end

787 788 789 790 791
    describe 'group access denied' do
      let(:group) { create(:group) }
      let(:user) { create(:user) }
      let(:group_member) do
        group.request_access(user)
792
        group.requesters.find_by(user_id: user.id)
793 794
      end
      subject { Notify.member_access_denied_email('group', group.id, user.id) }
795

796 797 798
      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"
799

800 801
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{group.name} group was denied"
802 803
        is_expected.to have_html_escaped_body_text group.name
        is_expected.to have_body_text group.web_url
804
      end
805 806
    end

807 808 809 810
    describe 'group access changed' do
      let(:group) { create(:group) }
      let(:user) { create(:user) }
      let(:group_member) { create(:group_member, group: group, user: user) }
811

812 813 814 815 816 817
      subject { Notify.member_access_granted_email('group', group_member.id) }

      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"

818 819
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{group.name} group was granted"
820 821 822
        is_expected.to have_html_escaped_body_text group.name
        is_expected.to have_body_text group.web_url
        is_expected.to have_body_text group_member.human_access
823
      end
824 825
    end

826 827 828 829 830 831 832 833 834
    def invite_to_group(group, inviter:)
      create(
        :group_member,
        :developer,
        group: group,
        invite_token: '1234',
        invite_email: 'toto@example.com',
        user: nil,
        created_by: inviter
835
      )
836 837
    end

838 839 840
    describe 'group invitation' do
      let(:group) { create(:group) }
      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
841
      let(:group_member) { invite_to_group(group, inviter: owner) }
842

843
      subject { Notify.member_invited_email('group', group_member.id, group_member.invite_token) }
844

845 846 847
      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"
848

849 850
      it 'contains all the useful information' do
        is_expected.to have_subject "Invitation to join the #{group.name} group"
851 852 853 854
        is_expected.to have_html_escaped_body_text group.name
        is_expected.to have_body_text group.web_url
        is_expected.to have_body_text group_member.human_access
        is_expected.to have_body_text group_member.invite_token
855
      end
856 857
    end

858 859
    describe 'group invitation accepted' do
      let(:group) { create(:group) }
860
      let(:invited_user) { create(:user, name: 'invited user') }
861 862
      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
      let(:group_member) do
863
        invitee = invite_to_group(group, inviter: owner)
864 865 866 867 868 869 870 871 872 873
        invitee.accept_invite!(invited_user)
        invitee
      end

      subject { Notify.member_invite_accepted_email('group', group_member.id) }

      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"

874 875
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation accepted'
876 877 878 879
        is_expected.to have_html_escaped_body_text group.name
        is_expected.to have_body_text group.web_url
        is_expected.to have_body_text group_member.invite_email
        is_expected.to have_html_escaped_body_text invited_user.name
880
      end
881 882
    end

883 884 885 886
    describe 'group invitation declined' do
      let(:group) { create(:group) }
      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
      let(:group_member) do
887
        invitee = invite_to_group(group, inviter: owner)
888 889 890 891 892 893 894 895 896 897
        invitee.decline_invite!
        invitee
      end

      subject { Notify.member_invite_declined_email('group', group.id, group_member.invite_email, owner.id) }

      it_behaves_like 'an email sent from GitLab'
      it_behaves_like 'it should not have Gmail Actions links'
      it_behaves_like "a user cannot unsubscribe through footer link"

898 899
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation declined'
900 901 902
        is_expected.to have_html_escaped_body_text group.name
        is_expected.to have_body_text group.web_url
        is_expected.to have_body_text group_member.invite_email
903
      end
904 905
    end
  end
906 907 908 909 910 911

  describe 'confirmation if email changed' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user, email: 'old-email@mail.com') }

    before do
912
      stub_config_setting(email_subject_suffix: 'A Nice Suffix')
913 914 915 916
      perform_enqueued_jobs do
        user.email = "new-email@mail.com"
        user.save
      end
917 918 919 920
    end

    subject { ActionMailer::Base.deliveries.last }

921
    it_behaves_like 'an email sent from GitLab'
922
    it_behaves_like "a user cannot unsubscribe through footer link"
923

924
    it 'is sent to the new user' do
925
      is_expected.to deliver_to 'new-email@mail.com'
926 927 928
    end

    it 'has the correct subject' do
929
      is_expected.to have_subject /^Confirmation instructions/
930 931 932
    end

    it 'includes a link to the site' do
933
      is_expected.to have_body_text /#{example_site_path}/
934 935
    end
  end
936

937 938 939 940 941
  describe 'email on push for a created branch' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
    let(:tree_path) { namespace_project_tree_path(project.namespace, project, "master") }

942
    subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :create) }
943

944
    it_behaves_like 'it should not have Gmail Actions links'
945
    it_behaves_like 'a user cannot unsubscribe through footer link'
946 947
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
948

949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
    end

    it 'has the correct subject' do
      is_expected.to have_subject /Pushed new branch master/
    end

    it 'contains a link to the branch' do
      is_expected.to have_body_text /#{tree_path}/
    end
  end

  describe 'email on push for a created tag' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
    let(:tree_path) { namespace_project_tree_path(project.namespace, project, "v1.0") }

969
    subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
970

971
    it_behaves_like 'it should not have Gmail Actions links'
972
    it_behaves_like "a user cannot unsubscribe through footer link"
973 974
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
975

976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
    end

    it 'has the correct subject' do
      is_expected.to have_subject /Pushed new tag v1\.0/
    end

    it 'contains a link to the tag' do
      is_expected.to have_body_text /#{tree_path}/
    end
  end

  describe 'email on push for a deleted branch' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }

995
    subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :delete) }
996

997
    it_behaves_like 'it should not have Gmail Actions links'
998
    it_behaves_like 'a user cannot unsubscribe through footer link'
999 1000
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1001

1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
    end

    it 'has the correct subject' do
      is_expected.to have_subject /Deleted branch master/
    end
  end

  describe 'email on push for a deleted tag' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }

1017
    subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
1018

1019
    it_behaves_like 'it should not have Gmail Actions links'
1020
    it_behaves_like 'a user cannot unsubscribe through footer link'
1021 1022
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1023

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
    end

    it 'has the correct subject' do
      is_expected.to have_subject /Deleted tag v1\.0/
    end
  end

1035
  describe 'email on push with multiple commits' do
1036 1037
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
1038 1039 1040
    let(:raw_compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_image_commit.id, sample_commit.id) }
    let(:compare) { Compare.decorate(raw_compare, project) }
    let(:commits) { compare.commits }
1041
    let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) }
1042
    let(:send_from_committer_email) { false }
1043
    let(:diff_refs) { Gitlab::Diff::DiffRefs.new(base_sha: project.merge_base_commit(sample_image_commit.id, sample_commit.id).id, head_sha: sample_commit.id) }
1044

1045
    subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, diff_refs: diff_refs, send_from_committer_email: send_from_committer_email) }
1046

1047
    it_behaves_like 'it should not have Gmail Actions links'
1048
    it_behaves_like 'a user cannot unsubscribe through footer link'
1049 1050
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1051

1052 1053
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
1054 1055
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
1056 1057
    end

1058
    it 'has the correct subject' do
1059
      is_expected.to have_subject /\[#{project.path_with_namespace}\]\[master\] #{commits.length} commits:/
1060 1061 1062
    end

    it 'includes commits list' do
1063
      is_expected.to have_body_text /Change some files/
1064 1065
    end

1066 1067
    it 'includes diffs with character-level highlighting' do
      is_expected.to have_body_text /def<\/span> <span class=\"nf\">archive_formats_regex/
1068
    end
1069 1070

    it 'contains a link to the diff' do
1071
      is_expected.to have_body_text /#{diff_path}/
1072
    end
1073

1074
    it 'does not contain the misleading footer' do
1075 1076
      is_expected.not_to have_body_text /you are a member of/
    end
1077 1078 1079 1080

    context "when set to send from committer email if domain matches" do
      let(:send_from_committer_email) { true }

1081 1082 1083 1084 1085
      before do
        allow(Gitlab.config.gitlab).to receive(:host).and_return("gitlab.corp.company.com")
      end

      context "when the committer email domain is within the GitLab domain" do
1086
        before do
1087
          user.update_attribute(:email, "user@company.com")
1088
          user.confirm
1089 1090 1091 1092 1093 1094
        end

        it "is sent from the committer email" do
          sender = subject.header[:from].addrs[0]
          expect(sender.address).to eq(user.email)
        end
1095 1096 1097 1098 1099

        it "is set to reply to the committer email" do
          sender = subject.header[:reply_to].addrs[0]
          expect(sender.address).to eq(user.email)
        end
1100 1101
      end

1102 1103 1104
      context "when the committer email domain is not completely within the GitLab domain" do
        before do
          user.update_attribute(:email, "user@something.company.com")
1105
          user.confirm
1106 1107 1108 1109 1110 1111
        end

        it "is sent from the default email" do
          sender = subject.header[:from].addrs[0]
          expect(sender.address).to eq(gitlab_sender)
        end
1112 1113 1114 1115 1116

        it "is set to reply to the default email" do
          sender = subject.header[:reply_to].addrs[0]
          expect(sender.address).to eq(gitlab_sender_reply_to)
        end
1117 1118 1119 1120 1121
      end

      context "when the committer email domain is outside the GitLab domain" do
        before do
          user.update_attribute(:email, "user@mpany.com")
1122
          user.confirm
1123
        end
1124 1125 1126 1127 1128

        it "is sent from the default email" do
          sender = subject.header[:from].addrs[0]
          expect(sender.address).to eq(gitlab_sender)
        end
1129 1130 1131 1132 1133

        it "is set to reply to the default email" do
          sender = subject.header[:reply_to].addrs[0]
          expect(sender.address).to eq(gitlab_sender_reply_to)
        end
1134 1135
      end
    end
1136
  end
1137 1138 1139 1140

  describe 'email on push with a single commit' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
1141 1142 1143
    let(:raw_compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_commit.parent_id, sample_commit.id) }
    let(:compare) { Compare.decorate(raw_compare, project) }
    let(:commits) { compare.commits }
Vinnie Okada committed
1144
    let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) }
1145
    let(:diff_refs) { Gitlab::Diff::DiffRefs.new(base_sha: project.merge_base_commit(sample_image_commit.id, sample_commit.id).id, head_sha: sample_commit.id) }
1146

1147
    subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, diff_refs: diff_refs) }
1148

1149
    it_behaves_like 'it should show Gmail Actions View Commit link'
1150
    it_behaves_like 'a user cannot unsubscribe through footer link'
1151 1152
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1153

1154 1155
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
1156 1157
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
1158 1159 1160
    end

    it 'has the correct subject' do
1161
      is_expected.to have_subject /#{commits.first.title}/
1162 1163 1164
    end

    it 'includes commits list' do
1165
      is_expected.to have_body_text /Change some files/
1166 1167
    end

1168 1169
    it 'includes diffs with character-level highlighting' do
      is_expected.to have_body_text /def<\/span> <span class=\"nf\">archive_formats_regex/
1170 1171 1172
    end

    it 'contains a link to the diff' do
1173
      is_expected.to have_body_text /#{diff_path}/
1174 1175
    end
  end
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209

  describe 'HTML emails setting' do
    let(:project) { create(:project) }
    let(:user) { create(:user) }
    let(:multipart_mail) { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") }

    context 'when disabled' do
      it 'only sends the text template' do
        stub_application_setting(html_emails_enabled: false)

        EmailTemplateInterceptor.delivering_email(multipart_mail)

        expect(multipart_mail).to have_part_with('text/plain')
        expect(multipart_mail).not_to have_part_with('text/html')
      end
    end

    context 'when enabled' do
      it 'sends a multipart message' do
        stub_application_setting(html_emails_enabled: true)

        EmailTemplateInterceptor.delivering_email(multipart_mail)

        expect(multipart_mail).to have_part_with('text/plain')
        expect(multipart_mail).to have_part_with('text/html')
      end
    end

    matcher :have_part_with do |expected|
      match do |actual|
        actual.body.parts.any? { |part| part.content_type.try(:match, %r(#{expected})) }
      end
    end
  end
1210
end