BigW Consortium Gitlab

notify_spec.rb 50.9 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 13 14 15 16 17 18 19
  def have_referable_subject(referable, reply: false)
    prefix = referable.project.name if referable.project
    prefix = "Re: #{prefix}" if reply

    suffix = "#{referable.title} (#{referable.to_reference})"

    have_subject [prefix, suffix].compact.join(' | ')
  end

20 21
  context 'for a project' do
    describe 'items that are assignable, the email' do
22
      let(:current_user) { create(:user, email: "current@email.com") }
23
      let(:assignee) { create(:user, email: 'assignee@example.com', name: 'John Doe') }
24
      let(:previous_assignee) { create(:user, name: 'Previous Assignee') }
25

26
      shared_examples 'an assignee email' do
27 28
        it 'is sent to the assignee as the author' do
          sender = subject.header[:from].addrs.first
29

30 31 32 33 34
          aggregate_failures do
            expect(sender.display_name).to eq(current_user.name)
            expect(sender.address).to eq(gitlab_sender)
            expect(subject).to deliver_to(assignee.email)
          end
35 36
        end
      end
37

38
      context 'for issues' do
39 40
        let(:issue) { create(:issue, author: current_user, assignees: [assignee], project: project) }
        let(:issue_with_description) { create(:issue, author: current_user, assignees: [assignee], project: project, description: 'My awesome description') }
41

42
        describe 'that are new' do
43
          subject { described_class.new_issue_email(issue.assignees.first.id, issue.id) }
44

45
          it_behaves_like 'an assignee email'
46 47 48
          it_behaves_like 'an email starting a new thread with reply-by-email enabled' do
            let(:model) { issue }
          end
49
          it_behaves_like 'it should show Gmail Actions View Issue link'
50
          it_behaves_like 'an unsubscribeable thread'
51

52 53 54 55 56
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(issue)
              is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
            end
57
          end
58 59 60

          context 'when enabled email_author_in_body' do
            before do
Sean McGivern committed
61
              stub_application_setting(email_author_in_body: true)
62 63 64
            end

            it 'contains a link to note author' do
65
              is_expected.to have_html_escaped_body_text(issue.author_name)
Douwe Maan committed
66
              is_expected.to have_body_text 'created an issue:'
67 68
            end
          end
69
        end
70

71
        describe 'that are new with a description' do
72
          subject { described_class.new_issue_email(issue_with_description.assignees.first.id, issue_with_description.id) }
73

74 75
          it_behaves_like 'it should show Gmail Actions View Issue link'

76
          it 'contains the description' do
77
            is_expected.to have_html_escaped_body_text issue_with_description.description
78 79 80
          end
        end

81
        describe 'that have been reassigned' do
82
          subject { described_class.reassigned_issue_email(recipient.id, issue.id, [previous_assignee.id], current_user.id) }
83 84

          it_behaves_like 'a multiple recipients email'
85 86 87
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
88
          it_behaves_like 'it should show Gmail Actions View Issue link'
89
          it_behaves_like 'an unsubscribeable thread'
90

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

97 98 99 100 101 102 103
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(issue, reply: true)
              is_expected.to have_html_escaped_body_text(previous_assignee.name)
              is_expected.to have_html_escaped_body_text(assignee.name)
              is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
            end
104 105
          end
        end
106

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

          it_behaves_like 'a multiple recipients email'
111 112 113
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
114 115 116 117 118 119 120 121 122 123
          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

124 125 126 127 128 129
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(issue, reply: true)
              is_expected.to have_body_text('foo, bar, and baz')
              is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
            end
130
          end
131 132

          context 'with a preferred language' do
133 134 135 136 137 138 139
            before do
              Gitlab::I18n.locale = :es
            end

            after do
              Gitlab::I18n.use_default_locale
            end
140 141 142 143 144

            it 'always generates the email using the default language' do
              is_expected.to have_body_text('foo, bar, and baz')
            end
          end
145 146
        end

147 148
        describe 'status changed' do
          let(:status) { 'closed' }
149
          subject { described_class.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) }
150

151 152 153
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
154
          it_behaves_like 'it should show Gmail Actions View Issue link'
155
          it_behaves_like 'an unsubscribeable thread'
156

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

163 164 165 166 167 168 169
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(issue, reply: true)
              is_expected.to have_body_text(status)
              is_expected.to have_html_escaped_body_text(current_user.name)
              is_expected.to have_body_text(namespace_project_issue_path project.namespace, project, issue)
            end
170
          end
171
        end
172 173 174

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

177 178 179
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { issue }
          end
180 181 182 183 184 185 186
          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

187
          it 'has the correct subject and body' do
188 189 190
            new_issue_url = namespace_project_issue_path(new_issue.project.namespace,
                                                         new_issue.project, new_issue)

191 192 193 194 195
            aggregate_failures do
              is_expected.to have_referable_subject(issue, reply: true)
              is_expected.to have_body_text(new_issue_url)
              is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
            end
196 197
          end
        end
198 199 200
      end

      context 'for merge requests' do
201
        let(:project) { create(:project, :repository) }
202
        let(:merge_author) { create(:user) }
203
        let(:merge_request) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project) }
204
        let(:merge_request_with_description) { create(:merge_request, author: current_user, assignee: assignee, source_project: project, target_project: project, description: 'My awesome description') }
205 206

        describe 'that are new' do
207
          subject { described_class.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
208 209

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

216 217 218 219 220 221 222
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(merge_request)
              is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
              is_expected.to have_body_text(merge_request.source_branch)
              is_expected.to have_body_text(merge_request.target_branch)
            end
223
          end
Philip Blatter committed
224

225 226
          context 'when enabled email_author_in_body' do
            before do
Sean McGivern committed
227
              stub_application_setting(email_author_in_body: true)
228 229 230
            end

            it 'contains a link to note author' do
231
              is_expected.to have_html_escaped_body_text merge_request.author_name
Douwe Maan committed
232
              is_expected.to have_body_text 'created a merge request:'
233 234
            end
          end
235 236
        end

237
        describe 'that are new with a description' do
238
          subject { described_class.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) }
239

240
          it_behaves_like 'it should show Gmail Actions View Merge request link'
241
          it_behaves_like "an unsubscribeable thread"
242

243
          it 'contains the description' do
244
            is_expected.to have_html_escaped_body_text merge_request_with_description.description
245 246 247
          end
        end

248
        describe 'that are reassigned' do
249
          subject { described_class.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
250 251

          it_behaves_like 'a multiple recipients email'
252 253 254
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
255
          it_behaves_like 'it should show Gmail Actions View Merge request link'
256
          it_behaves_like "an unsubscribeable thread"
257

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

264 265 266 267 268 269 270
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(merge_request, reply: true)
              is_expected.to have_html_escaped_body_text(previous_assignee.name)
              is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
              is_expected.to have_html_escaped_body_text(assignee.name)
            end
271
          end
272 273
        end

274
        describe 'that have been relabeled' do
275
          subject { described_class.relabeled_merge_request_email(recipient.id, merge_request.id, %w[foo bar baz], current_user.id) }
276 277

          it_behaves_like 'a multiple recipients email'
278 279 280
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
281 282 283 284 285 286 287 288 289 290
          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

291
          it 'has the correct subject and body' do
292
            is_expected.to have_referable_subject(merge_request, reply: true)
293 294
            is_expected.to have_body_text('foo, bar, and baz')
            is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
295 296 297
          end
        end

298 299
        describe 'status changed' do
          let(:status) { 'reopened' }
300
          subject { described_class.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) }
301

302 303 304
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
305
          it_behaves_like 'it should show Gmail Actions View Merge request link'
306
          it_behaves_like 'an unsubscribeable thread'
307 308 309

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

314 315 316 317 318 319 320
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(merge_request, reply: true)
              is_expected.to have_body_text(status)
              is_expected.to have_html_escaped_body_text(current_user.name)
              is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
            end
321 322 323
          end
        end

324
        describe 'that are merged' do
325
          subject { described_class.merged_merge_request_email(recipient.id, merge_request.id, merge_author.id) }
326 327

          it_behaves_like 'a multiple recipients email'
328 329 330
          it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
            let(:model) { merge_request }
          end
331
          it_behaves_like 'it should show Gmail Actions View Merge request link'
332
          it_behaves_like 'an unsubscribeable thread'
333 334 335

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

340 341 342 343 344 345
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(merge_request, reply: true)
              is_expected.to have_body_text('merged')
              is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
            end
346
          end
347 348
        end
      end
349 350
    end

351
    describe 'project was moved' do
352
      let(:project) { create(:empty_project) }
353
      let(:user) { create(:user) }
354
      subject { described_class.project_was_moved_email(project.id, user.id, "gitlab/gitlab") }
355

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

360 361
      it 'has the correct subject and body' do
        is_expected.to have_subject("#{project.name} | Project was moved")
362
        is_expected.to have_html_escaped_body_text project.name_with_namespace
363
        is_expected.to have_body_text(project.ssh_url_to_repo)
364 365 366
      end
    end

367
    describe 'project access requested' do
368
      context 'for a project in a user namespace' do
369 370 371 372 373 374
        let(:project) do
          create(:empty_project, :public, :access_requestable) do |project|
            project.team << [project.owner, :master, project.owner]
          end
        end

375 376 377
        let(:user) { create(:user) }
        let(:project_member) do
          project.request_access(user)
378
          project.requesters.find_by(user_id: user.id)
379
        end
380
        subject { described_class.member_access_requested_email('project', project_member.id) }
381 382 383 384 385 386 387 388 389 390 391

        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"
392
          is_expected.to have_html_escaped_body_text project.name_with_namespace
393 394
          is_expected.to have_body_text namespace_project_project_members_url(project.namespace, project)
          is_expected.to have_body_text project_member.human_access
395
        end
396 397
      end

398 399 400
      context 'for a project in a group' do
        let(:group_owner) { create(:user) }
        let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } }
401
        let(:project) { create(:empty_project, :public, :access_requestable, namespace: group) }
402 403 404
        let(:user) { create(:user) }
        let(:project_member) do
          project.request_access(user)
405
          project.requesters.find_by(user_id: user.id)
406
        end
407
        subject { described_class.member_access_requested_email('project', project_member.id) }
408

409 410 411 412 413 414 415 416 417 418
        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"
419
          is_expected.to have_html_escaped_body_text project.name_with_namespace
420 421
          is_expected.to have_body_text namespace_project_project_members_url(project.namespace, project)
          is_expected.to have_body_text project_member.human_access
422
        end
423
      end
424 425 426
    end

    describe 'project access denied' do
427
      let(:project) { create(:empty_project, :public, :access_requestable) }
428 429 430
      let(:user) { create(:user) }
      let(:project_member) do
        project.request_access(user)
431
        project.requesters.find_by(user_id: user.id)
432
      end
433
      subject { described_class.member_access_denied_email('project', project.id, user.id) }
434 435 436 437 438

      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"

439 440
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was denied"
441
        is_expected.to have_html_escaped_body_text project.name_with_namespace
442
        is_expected.to have_body_text project.web_url
443
      end
444 445
    end

446
    describe 'project access changed' do
447 448
      let(:owner) { create(:user, name: "Chang O'Keefe") }
      let(:project) { create(:empty_project, :public, :access_requestable, namespace: owner.namespace) }
449
      let(:user) { create(:user) }
450
      let(:project_member) { create(:project_member, project: project, user: user) }
451
      subject { described_class.member_access_granted_email('project', project_member.id) }
452 453

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

457 458
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was granted"
459
        is_expected.to have_html_escaped_body_text project.name_with_namespace
460 461
        is_expected.to have_body_text project.web_url
        is_expected.to have_body_text project_member.human_access
462
      end
463
    end
464

465 466 467 468 469 470 471 472 473
    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
474
      )
475 476 477
    end

    describe 'project invitation' do
478
      let(:project) { create(:empty_project) }
479
      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
480
      let(:project_member) { invite_to_project(project, inviter: master) }
481

482
      subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) }
483 484 485 486 487

      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"

488 489
      it 'contains all the useful information' do
        is_expected.to have_subject "Invitation to join the #{project.name_with_namespace} project"
490
        is_expected.to have_html_escaped_body_text project.name_with_namespace
491 492 493
        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
494
      end
495 496 497
    end

    describe 'project invitation accepted' do
498
      let(:project) { create(:empty_project) }
499
      let(:invited_user) { create(:user, name: 'invited user') }
500 501
      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
      let(:project_member) do
502
        invitee = invite_to_project(project, inviter: master)
503 504
        invitee.accept_invite!(invited_user)
        invitee
505
      end
506

507
      subject { described_class.member_invite_accepted_email('project', project_member.id) }
508 509 510 511 512

      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"

513 514
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation accepted'
515
        is_expected.to have_html_escaped_body_text project.name_with_namespace
516 517
        is_expected.to have_body_text project.web_url
        is_expected.to have_body_text project_member.invite_email
518
        is_expected.to have_html_escaped_body_text invited_user.name
519
      end
520 521 522
    end

    describe 'project invitation declined' do
523
      let(:project) { create(:empty_project) }
524 525
      let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
      let(:project_member) do
526
        invitee = invite_to_project(project, inviter: master)
527 528
        invitee.decline_invite!
        invitee
529
      end
530

531
      subject { described_class.member_invite_declined_email('project', project.id, project_member.invite_email, master.id) }
532 533 534 535 536

      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"

537 538
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation declined'
539
        is_expected.to have_html_escaped_body_text project.name_with_namespace
540 541
        is_expected.to have_body_text project.web_url
        is_expected.to have_body_text project_member.invite_email
542
      end
543 544
    end

545
    context 'items that are noteable, the email for a note' do
546 547
      let(:note_author) { create(:user, name: 'author_name') }
      let(:note) { create(:note, project: project, author: note_author) }
548

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

553
      shared_examples 'a note email' do
554 555
        it_behaves_like 'it should have Gmail Actions links'

556
        it 'is sent to the given recipient as the author' do
557 558
          sender = subject.header[:from].addrs[0]

559 560 561 562 563
          aggregate_failures do
            expect(sender.display_name).to eq(note_author.name)
            expect(sender.address).to eq(gitlab_sender)
            expect(subject).to deliver_to(recipient.notification_email)
          end
564 565 566
        end

        it 'contains the message from the note' do
567
          is_expected.to have_html_escaped_body_text note.note
568
        end
569

570
        it 'does not contain note author' do
Douwe Maan committed
571
          is_expected.not_to have_body_text note.author_name
572 573 574 575
        end

        context 'when enabled email_author_in_body' do
          before do
Sean McGivern committed
576
            stub_application_setting(email_author_in_body: true)
577 578 579
          end

          it 'contains a link to note author' do
580
            is_expected.to have_html_escaped_body_text note.author_name
581 582
          end
        end
583 584 585
      end

      describe 'on a commit' do
586
        let(:project) { create(:project, :repository) }
587
        let(:commit) { project.commit }
Dmitriy Zaporozhets committed
588

589 590 591
        before do
          allow(note).to receive(:noteable).and_return(commit)
        end
592

593
        subject { described_class.note_commit_email(recipient.id, note.id) }
594 595

        it_behaves_like 'a note email'
596 597 598
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { commit }
        end
599
        it_behaves_like 'it should show Gmail Actions View Commit link'
600
        it_behaves_like 'a user cannot unsubscribe through footer link'
601

602 603 604 605 606
        it 'has the correct subject and body' do
          aggregate_failures do
            is_expected.to have_subject("Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})")
            is_expected.to have_body_text(commit.short_id)
          end
607 608 609 610
        end
      end

      describe 'on a merge request' do
611
        let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
Vinnie Okada committed
612
        let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") }
613 614 615 616

        before do
          allow(note).to receive(:noteable).and_return(merge_request)
        end
617

618
        subject { described_class.note_merge_request_email(recipient.id, note.id) }
Sean McGivern committed
619

620
        it_behaves_like 'a note email'
621 622 623
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { merge_request }
        end
624
        it_behaves_like 'it should show Gmail Actions View Merge request link'
625
        it_behaves_like 'an unsubscribeable thread'
626

627 628 629 630 631
        it 'has the correct subject and body' do
          aggregate_failures do
            is_expected.to have_referable_subject(merge_request, reply: true)
            is_expected.to have_body_text note_on_merge_request_path
          end
632 633 634 635
        end
      end

      describe 'on an issue' do
636
        let(:issue) { create(:issue, project: project) }
Vinnie Okada committed
637
        let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") }
638 639 640 641

        before do
          allow(note).to receive(:noteable).and_return(issue)
        end
642

643
        subject { described_class.note_issue_email(recipient.id, note.id) }
644 645

        it_behaves_like 'a note email'
646 647 648
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { issue }
        end
649
        it_behaves_like 'it should show Gmail Actions View Issue link'
650
        it_behaves_like 'an unsubscribeable thread'
651

652 653 654 655 656
        it 'has the correct subject and body' do
          aggregate_failures do
            is_expected.to have_referable_subject(issue, reply: true)
            is_expected.to have_body_text(note_on_issue_path)
          end
657 658
        end
      end
659
    end
660

Douwe Maan committed
661
    context 'items that are noteable, the email for a discussion note' do
662
      let(:project) { create(:project, :repository) }
663 664 665 666 667 668
      let(:note_author) { create(:user, name: 'author_name') }

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

Douwe Maan committed
669 670 671
      shared_examples 'a discussion note email' do |model|
        it_behaves_like 'it should have Gmail Actions links'

Douwe Maan committed
672
        it 'is sent to the given recipient as the author' do
Douwe Maan committed
673 674
          sender = subject.header[:from].addrs[0]

Douwe Maan committed
675 676 677 678 679
          aggregate_failures do
            expect(sender.display_name).to eq(note_author.name)
            expect(sender.address).to eq(gitlab_sender)
            expect(subject).to deliver_to(recipient.notification_email)
          end
Douwe Maan committed
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
        end

        it 'contains the message from the note' do
          is_expected.to have_body_text note.note
        end

        it 'contains an introduction' do
          is_expected.to have_body_text 'started a new discussion'
        end

        context 'when a comment on an existing discussion' do
          let!(:second_note) { create(model, author: note_author, noteable: nil, in_reply_to: note) }

          it 'contains an introduction' do
            is_expected.to have_body_text 'commented on a'
          end
        end
      end

      describe 'on a commit' do
        let(:commit) { project.commit }
        let(:note) { create(:discussion_note_on_commit, commit_id: commit.id, project: project, author: note_author) }

703 704 705
        before do
          allow(note).to receive(:noteable).and_return(commit)
        end
Douwe Maan committed
706

707
        subject { described_class.note_commit_email(recipient.id, note.id) }
Douwe Maan committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728

        it_behaves_like 'a discussion note email', :discussion_note_on_commit
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { commit }
        end
        it_behaves_like 'it should show Gmail Actions View Commit link'
        it_behaves_like 'a user cannot unsubscribe through footer link'

        it 'has the correct subject' do
          is_expected.to have_subject "Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})"
        end

        it 'contains a link to the commit' do
          is_expected.to have_body_text commit.short_id
        end
      end

      describe 'on a merge request' do
        let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
        let(:note) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: note_author) }
        let(:note_on_merge_request_path) { namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: "note_#{note.id}") }
729 730 731 732

        before do
          allow(note).to receive(:noteable).and_return(merge_request)
        end
Douwe Maan committed
733

734
        subject { described_class.note_merge_request_email(recipient.id, note.id) }
Douwe Maan committed
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755

        it_behaves_like 'a discussion note email', :discussion_note_on_merge_request
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { merge_request }
        end
        it_behaves_like 'it should show Gmail Actions View Merge request link'
        it_behaves_like 'an unsubscribeable thread'

        it 'has the correct subject' do
          is_expected.to have_referable_subject(merge_request, reply: true)
        end

        it 'contains a link to the merge request note' do
          is_expected.to have_body_text note_on_merge_request_path
        end
      end

      describe 'on an issue' do
        let(:issue) { create(:issue, project: project) }
        let(:note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: note_author) }
        let(:note_on_issue_path) { namespace_project_issue_path(project.namespace, project, issue, anchor: "note_#{note.id}") }
756 757 758 759

        before do
          allow(note).to receive(:noteable).and_return(issue)
        end
Douwe Maan committed
760

761
        subject { described_class.note_issue_email(recipient.id, note.id) }
Douwe Maan committed
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787

        it_behaves_like 'a discussion note email', :discussion_note_on_issue
        it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
          let(:model) { issue }
        end
        it_behaves_like 'it should show Gmail Actions View Issue link'
        it_behaves_like 'an unsubscribeable thread'

        it 'has the correct subject' do
          is_expected.to have_referable_subject(issue, reply: true)
        end

        it 'contains a link to the issue note' do
          is_expected.to have_body_text note_on_issue_path
        end
      end
    end

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

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

      shared_examples 'an email for a note on a diff discussion' do  |model|
Douwe Maan committed
788
        let(:note) { create(model, author: note_author) }
789 790

        it "includes diffs with character-level highlighting" do
791
          is_expected.to have_body_text '<span class="p">}</span></span>'
792 793 794
        end

        it 'contains a link to the diff file' do
795
          is_expected.to have_body_text note.diff_file.file_path
796 797 798 799
        end

        it_behaves_like 'it should have Gmail Actions links'

800
        it 'is sent to the given recipient as the author' do
801 802
          sender = subject.header[:from].addrs[0]

803 804 805 806 807
          aggregate_failures do
            expect(sender.display_name).to eq(note_author.name)
            expect(sender.address).to eq(gitlab_sender)
            expect(subject).to deliver_to(recipient.notification_email)
          end
808 809 810
        end

        it 'contains the message from the note' do
811
          is_expected.to have_html_escaped_body_text note.note
812 813
        end

Douwe Maan committed
814 815
        it 'contains an introduction' do
          is_expected.to have_body_text 'started a new discussion on'
816 817
        end

Douwe Maan committed
818 819
        context 'when a comment on an existing discussion' do
          let!(:second_note) { create(model, author: note_author, noteable: nil, in_reply_to: note) }
820

Douwe Maan committed
821 822
          it 'contains an introduction' do
            is_expected.to have_body_text 'commented on a discussion on'
823 824 825 826 827 828 829 830
          end
        end
      end

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

831
        subject { described_class.note_commit_email(recipient.id, note.id) }
832

Douwe Maan committed
833
        it_behaves_like 'an email for a note on a diff discussion', :diff_note_on_commit
834 835 836 837 838 839 840 841
        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) }

842
        subject { described_class.note_merge_request_email(recipient.id, note.id) }
843

Douwe Maan committed
844
        it_behaves_like 'an email for a note on a diff discussion', :diff_note_on_merge_request
845 846 847 848
        it_behaves_like 'it should show Gmail Actions View Merge request link'
        it_behaves_like 'an unsubscribeable thread'
      end
    end
849
  end
850

851 852
  context 'for a group' do
    describe 'group access requested' do
853
      let(:group) { create(:group, :public, :access_requestable) }
854 855 856
      let(:user) { create(:user) }
      let(:group_member) do
        group.request_access(user)
857
        group.requesters.find_by(user_id: user.id)
858
      end
859
      subject { described_class.member_access_requested_email('group', group_member.id) }
860

861 862 863
      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"
864

865 866
      it 'contains all the useful information' do
        is_expected.to have_subject "Request to join the #{group.name} group"
867
        is_expected.to have_html_escaped_body_text group.name
868 869
        is_expected.to have_body_text group_group_members_url(group)
        is_expected.to have_body_text group_member.human_access
870
      end
871 872
    end

873 874 875 876 877
    describe 'group access denied' do
      let(:group) { create(:group) }
      let(:user) { create(:user) }
      let(:group_member) do
        group.request_access(user)
878
        group.requesters.find_by(user_id: user.id)
879
      end
880
      subject { described_class.member_access_denied_email('group', group.id, user.id) }
881

882 883 884
      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"
885

886 887
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{group.name} group was denied"
888
        is_expected.to have_html_escaped_body_text group.name
889
        is_expected.to have_body_text group.web_url
890
      end
891 892
    end

893 894 895 896
    describe 'group access changed' do
      let(:group) { create(:group) }
      let(:user) { create(:user) }
      let(:group_member) { create(:group_member, group: group, user: user) }
897

898
      subject { described_class.member_access_granted_email('group', group_member.id) }
899 900 901 902 903

      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"

904 905
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{group.name} group was granted"
906
        is_expected.to have_html_escaped_body_text group.name
907 908
        is_expected.to have_body_text group.web_url
        is_expected.to have_body_text group_member.human_access
909
      end
910 911
    end

912 913 914 915 916 917 918 919 920
    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
921
      )
922 923
    end

924 925 926
    describe 'group invitation' do
      let(:group) { create(:group) }
      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
927
      let(:group_member) { invite_to_group(group, inviter: owner) }
928

929
      subject { described_class.member_invited_email('group', group_member.id, group_member.invite_token) }
930

931 932 933
      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"
934

935 936
      it 'contains all the useful information' do
        is_expected.to have_subject "Invitation to join the #{group.name} group"
937
        is_expected.to have_html_escaped_body_text group.name
938 939 940
        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
941
      end
942 943
    end

944 945
    describe 'group invitation accepted' do
      let(:group) { create(:group) }
946
      let(:invited_user) { create(:user, name: 'invited user') }
947 948
      let(:owner) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::OWNER) } }
      let(:group_member) do
949
        invitee = invite_to_group(group, inviter: owner)
950 951 952 953
        invitee.accept_invite!(invited_user)
        invitee
      end

954
      subject { described_class.member_invite_accepted_email('group', group_member.id) }
955 956 957 958 959

      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"

960 961
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation accepted'
962
        is_expected.to have_html_escaped_body_text group.name
963 964
        is_expected.to have_body_text group.web_url
        is_expected.to have_body_text group_member.invite_email
965
        is_expected.to have_html_escaped_body_text invited_user.name
966
      end
967 968
    end

969 970 971 972
    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
973
        invitee = invite_to_group(group, inviter: owner)
974 975 976 977
        invitee.decline_invite!
        invitee
      end

978
      subject { described_class.member_invite_declined_email('group', group.id, group_member.invite_email, owner.id) }
979 980 981 982 983

      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"

984 985
      it 'contains all the useful information' do
        is_expected.to have_subject 'Invitation declined'
986
        is_expected.to have_html_escaped_body_text group.name
987 988
        is_expected.to have_body_text group.web_url
        is_expected.to have_body_text group_member.invite_email
989
      end
990 991
    end
  end
992 993 994 995 996 997

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

    before do
998
      stub_config_setting(email_subject_suffix: 'A Nice Suffix')
999 1000 1001 1002
      perform_enqueued_jobs do
        user.email = "new-email@mail.com"
        user.save
      end
1003 1004 1005 1006
    end

    subject { ActionMailer::Base.deliveries.last }

1007
    it_behaves_like 'an email sent from GitLab'
1008
    it_behaves_like "a user cannot unsubscribe through footer link"
1009

1010
    it 'is sent to the new user' do
1011
      is_expected.to deliver_to 'new-email@mail.com'
1012 1013
    end

1014 1015 1016 1017 1018
    it 'has the correct subject and body' do
      aggregate_failures do
        is_expected.to have_subject('Confirmation instructions | A Nice Suffix')
        is_expected.to have_body_text(example_site_path)
      end
1019 1020
    end
  end
1021

1022 1023 1024
  describe 'email on push for a created branch' do
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
1025
    let(:tree_path) { namespace_project_tree_path(project.namespace, project, "empty-branch") }
1026

1027
    subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/empty-branch', action: :create) }
1028

1029
    it_behaves_like 'it should not have Gmail Actions links'
1030
    it_behaves_like 'a user cannot unsubscribe through footer link'
1031 1032
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1033

1034 1035 1036 1037 1038 1039
    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

1040 1041 1042 1043 1044
    it 'has the correct subject and body' do
      aggregate_failures do
        is_expected.to have_subject("[Git][#{project.full_path}] Pushed new branch empty-branch")
        is_expected.to have_body_text(tree_path)
      end
1045 1046 1047 1048 1049 1050 1051 1052
    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") }

1053
    subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
1054

1055
    it_behaves_like 'it should not have Gmail Actions links'
1056
    it_behaves_like "a user cannot unsubscribe through footer link"
1057 1058
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1059

1060 1061 1062 1063 1064 1065
    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

1066 1067 1068 1069 1070
    it 'has the correct subject and body' do
      aggregate_failures do
        is_expected.to have_subject("[Git][#{project.full_path}] Pushed new tag v1.0")
        is_expected.to have_body_text(tree_path)
      end
1071 1072 1073 1074 1075 1076 1077
    end
  end

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

1078
    subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :delete) }
1079

1080
    it_behaves_like 'it should not have Gmail Actions links'
1081
    it_behaves_like 'a user cannot unsubscribe through footer link'
1082 1083
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1084

1085 1086 1087 1088 1089 1090 1091
    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
1092
      is_expected.to have_subject "[Git][#{project.full_path}] Deleted branch master"
1093 1094 1095 1096 1097 1098 1099
    end
  end

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

1100
    subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
1101

1102
    it_behaves_like 'it should not have Gmail Actions links'
1103
    it_behaves_like 'a user cannot unsubscribe through footer link'
1104 1105
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1106

1107 1108 1109 1110 1111 1112 1113
    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
1114
      is_expected.to have_subject "[Git][#{project.full_path}] Deleted tag v1.0"
1115 1116 1117
    end
  end

1118
  describe 'email on push with multiple commits' do
1119
    let(:project) { create(:project, :repository) }
1120 1121
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
1122 1123 1124
    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 }
1125
    let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) }
1126
    let(:send_from_committer_email) { false }
1127
    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) }
1128

1129
    subject { described_class.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) }
1130

1131
    it_behaves_like 'it should not have Gmail Actions links'
1132
    it_behaves_like 'a user cannot unsubscribe through footer link'
1133 1134
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1135

1136 1137
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
1138 1139
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
1140 1141
    end

1142 1143 1144 1145 1146 1147 1148 1149
    it 'has the correct subject and body' do
      aggregate_failures do
        is_expected.to have_subject("[Git][#{project.full_path}][master] #{commits.length} commits: Ruby files modified")
        is_expected.to have_body_text('Change some files')
        is_expected.to have_body_text('def</span> <span class="nf">archive_formats_regex')
        is_expected.to have_body_text(diff_path)
        is_expected.not_to have_body_text('you are a member of')
      end
1150
    end
1151 1152 1153 1154

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

1155 1156 1157 1158 1159
      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
1160
        before do
1161
          user.update_attribute(:email, "user@company.com")
1162
          user.confirm
1163 1164 1165
        end

        it "is sent from the committer email" do
1166 1167
          from  = subject.header[:from].addrs.first
          reply = subject.header[:reply_to].addrs.first
1168

1169 1170 1171 1172
          aggregate_failures do
            expect(from.address).to eq(user.email)
            expect(reply.address).to eq(user.email)
          end
1173
        end
1174 1175
      end

1176 1177 1178
      context "when the committer email domain is not completely within the GitLab domain" do
        before do
          user.update_attribute(:email, "user@something.company.com")
1179
          user.confirm
1180 1181 1182
        end

        it "is sent from the default email" do
1183 1184
          from  = subject.header[:from].addrs.first
          reply = subject.header[:reply_to].addrs.first
1185

1186 1187 1188 1189
          aggregate_failures do
            expect(from.address).to eq(gitlab_sender)
            expect(reply.address).to eq(gitlab_sender_reply_to)
          end
1190
        end
1191 1192 1193 1194 1195
      end

      context "when the committer email domain is outside the GitLab domain" do
        before do
          user.update_attribute(:email, "user@mpany.com")
1196
          user.confirm
1197
        end
1198 1199

        it "is sent from the default email" do
1200 1201
          from = subject.header[:from].addrs.first
          reply = subject.header[:reply_to].addrs.first
1202

1203 1204 1205 1206
          aggregate_failures do
            expect(from.address).to eq(gitlab_sender)
            expect(reply.address).to eq(gitlab_sender_reply_to)
          end
1207
        end
1208 1209
      end
    end
1210
  end
1211 1212

  describe 'email on push with a single commit' do
1213
    let(:project) { create(:project, :repository) }
1214 1215
    let(:example_site_path) { root_path }
    let(:user) { create(:user) }
1216 1217 1218
    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
1219
    let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) }
1220
    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) }
1221

1222
    subject { described_class.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, diff_refs: diff_refs) }
1223

1224
    it_behaves_like 'it should show Gmail Actions View Commit link'
1225
    it_behaves_like 'a user cannot unsubscribe through footer link'
1226 1227
    it_behaves_like 'an email with X-GitLab headers containing project details'
    it_behaves_like 'an email that contains a header with author username'
1228

1229 1230
    it 'is sent as the author' do
      sender = subject.header[:from].addrs[0]
1231 1232
      expect(sender.display_name).to eq(user.name)
      expect(sender.address).to eq(gitlab_sender)
1233 1234
    end

1235 1236 1237 1238 1239 1240 1241
    it 'has the correct subject and body' do
      aggregate_failures do
        is_expected.to have_subject("[Git][#{project.full_path}][master] #{commits.first.title}")
        is_expected.to have_body_text('Change some files')
        is_expected.to have_body_text('def</span> <span class="nf">archive_formats_regex')
        is_expected.to have_body_text(diff_path)
      end
1242 1243
    end
  end
1244 1245

  describe 'HTML emails setting' do
1246
    let(:project) { create(:empty_project) }
1247
    let(:user) { create(:user) }
1248
    let(:multipart_mail) { described_class.project_was_moved_email(project.id, user.id, "gitlab/gitlab") }
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277

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