BigW Consortium Gitlab

notify_spec.rb 46.3 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 57 58 59 60
            end

            it 'contains a link to note author' do
              is_expected.to have_body_text issue.author_name
              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_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_body_text /#{previous_assignee.name}/
95 96 97
          end

          it 'contains the name of the new assignee' do
98
            is_expected.to have_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_body_text /#{current_user.name}/i
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 236 237 238 239
            end

            it 'contains a link to note author' do
              is_expected.to have_body_text merge_request.author_name
              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_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_body_text /#{previous_assignee.name}/
275 276 277
          end

          it 'contains the name of the new assignee' do
278
            is_expected.to have_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_body_text /#{current_user.name}/i
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_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 427 428 429 430
        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"
          is_expected.to have_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}/
        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 454 455 456 457
        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"
          is_expected.to have_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}/
        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 476 477 478
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was denied"
        is_expected.to have_body_text /#{project.name_with_namespace}/
        is_expected.to have_body_text /#{project.web_url}/
      end
479 480
    end

481
    describe 'project access changed' do
482
      let(:project) { create(:empty_project, :public, :access_requestable) }
483
      let(:user) { create(:user) }
484
      let(:project_member) { create(:project_member, project: project, user: user) }
485
      subject { Notify.member_access_granted_email('project', project_member.id) }
486 487

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

491 492 493 494 495 496
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was granted"
        is_expected.to have_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}/
      end
497
    end
498

499 500 501 502 503 504 505 506 507
    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
508
      )
509 510 511 512 513
    end

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

      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"

522 523 524 525 526 527 528
      it 'contains all the useful information' do
        is_expected.to have_subject "Invitation to join the #{project.name_with_namespace} project"
        is_expected.to have_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}/
      end
529 530 531 532
    end

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

541 542 543 544 545 546
      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"

547 548 549 550 551 552 553
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation accepted'
        is_expected.to have_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_body_text /#{invited_user.name}/
      end
554 555 556 557 558 559
    end

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

      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"

571 572 573 574 575 576
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation declined'
        is_expected.to have_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}/
      end
577 578
    end

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

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

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

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

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

        it 'contains the message from the note' do
601
          is_expected.to have_body_text /#{note.note}/
602
        end
603

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    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
697
      shared_examples 'a note email on a diff' do  |model|
698 699 700
        let(:note) { create(model, project: project, author: note_author) }

        it "includes diffs with character-level highlighting" do
701
          is_expected.to have_body_text /<span class=\"p\">}<\/span><\/span>/
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
        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
          is_expected.to have_body_text /#{note.note}/
        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
730
            stub_application_setting(email_author_in_body: true)
731 732 733 734 735 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
          end

          it 'contains a link to note author' do
            is_expected.to have_body_text note.author_name
            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
762
  end
763

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

774 775 776
      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"
777

778 779 780 781 782 783
      it 'contains all the useful information' do
        is_expected.to have_subject "Request to join the #{group.name} group"
        is_expected.to have_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}/
      end
784 785
    end

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

795 796 797
      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"
798

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

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

811 812 813 814 815 816
      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"

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

825 826 827 828 829 830 831 832 833
    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
834
      )
835 836
    end

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

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

844 845 846
      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"
847

848 849 850 851 852 853 854
      it 'contains all the useful information' do
        is_expected.to have_subject "Invitation to join the #{group.name} group"
        is_expected.to have_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}/
      end
855 856
    end

857 858
    describe 'group invitation accepted' do
      let(:group) { create(:group) }
859
      let(:invited_user) { create(:user, name: 'invited user') }
860 861
      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
      let(:group_member) do
862
        invitee = invite_to_group(group, inviter: owner)
863 864 865 866 867 868 869 870 871 872
        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"

873 874 875 876 877 878 879
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation accepted'
        is_expected.to have_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_body_text /#{invited_user.name}/
      end
880 881
    end

882 883 884 885
    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
886
        invitee = invite_to_group(group, inviter: owner)
887 888 889 890 891 892 893 894 895 896
        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"

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

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

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

    subject { ActionMailer::Base.deliveries.last }

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

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

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

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

936 937 938 939 940
  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") }

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

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

948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
    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") }

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

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

975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
    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) }

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

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

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
    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) }

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

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

1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
    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

1034
  describe 'email on push with multiple commits' do
1035 1036
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
1037 1038 1039
    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 }
1040
    let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) }
1041
    let(:send_from_committer_email) { false }
1042
    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) }
1043

1044
    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) }
1045

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

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

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

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

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

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

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

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

1080 1081 1082 1083 1084
      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
1085
        before do
1086
          user.update_attribute(:email, "user@company.com")
1087
          user.confirm
1088 1089 1090 1091 1092 1093
        end

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

        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
1099 1100
      end

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

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

        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
1116 1117 1118 1119 1120
      end

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

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

        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
1133 1134
      end
    end
1135
  end
1136 1137 1138 1139

  describe 'email on push with a single commit' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
1140 1141 1142
    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
1143
    let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) }
1144
    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) }
1145

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

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

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

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

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

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

    it 'contains a link to the diff' do
1172
      is_expected.to have_body_text /#{diff_path}/
1173 1174
    end
  end
1175 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

  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
1209
end