GitLab steht wegen Wartungsarbeiten am Montag, den 10. Mai, zwischen 17:00 und 19:00 Uhr nicht zur Verfügung.

notify_spec.rb 50.2 KB
Newer Older
1
require 'spec_helper'
Robert Speicher's avatar
Robert Speicher committed
2
require 'email_spec'
3 4 5 6

describe Notify do
  include EmailSpec::Helpers
  include EmailSpec::Matchers
Dmitriy Zaporozhets's avatar
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

Robb Kidd's avatar
Robb Kidd committed
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

Robb Kidd's avatar
Robb Kidd committed
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
Robb Kidd's avatar
Robb Kidd committed
35 36
        end
      end
37

Robb Kidd's avatar
Robb Kidd committed
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

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

Robb Kidd's avatar
Robb Kidd committed
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
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(issue)
55
              is_expected.to have_body_text(project_issue_path(project, issue))
56
            end
Robb Kidd's avatar
Robb Kidd committed
57
          end
58 59 60

          context 'when enabled email_author_in_body' do
            before do
Sean McGivern's avatar
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's avatar
Douwe Maan committed
66
              is_expected.to have_body_text 'created an issue:'
67 68
            end
          end
Robb Kidd's avatar
Robb Kidd committed
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

Robb Kidd's avatar
Robb Kidd committed
81
        describe 'that have been reassigned' do
82
          subject { described_class.reassigned_issue_email(recipient.id, issue.id, [previous_assignee.id], current_user.id) }
Robb Kidd's avatar
Robb Kidd committed
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'
Robb Kidd's avatar
Robb Kidd committed
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
          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)
102
              is_expected.to have_body_text(project_issue_path(project, issue))
103
            end
Robb Kidd's avatar
Robb Kidd committed
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
          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')
128
              is_expected.to have_body_text(project_issue_path(project, issue))
129
            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
          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)
168
              is_expected.to have_body_text(project_issue_path project, issue)
169
            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
            new_issue_url = project_issue_path(new_issue.project, new_issue)
189

190 191 192
            aggregate_failures do
              is_expected.to have_referable_subject(issue, reply: true)
              is_expected.to have_body_text(new_issue_url)
193
              is_expected.to have_body_text(project_issue_path(project, issue))
194
            end
195 196
          end
        end
Robb Kidd's avatar
Robb Kidd committed
197 198 199
      end

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

        describe 'that are new' do
206
          subject { described_class.new_merge_request_email(merge_request.assignee_id, merge_request.id) }
Robb Kidd's avatar
Robb Kidd committed
207 208

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

215 216 217
          it 'has the correct subject and body' do
            aggregate_failures do
              is_expected.to have_referable_subject(merge_request)
218
              is_expected.to have_body_text(project_merge_request_path(project, merge_request))
219 220 221
              is_expected.to have_body_text(merge_request.source_branch)
              is_expected.to have_body_text(merge_request.target_branch)
            end
Robb Kidd's avatar
Robb Kidd committed
222
          end
Philip Blatter's avatar
Philip Blatter committed
223

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

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

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

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

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

Robb Kidd's avatar
Robb Kidd committed
247
        describe 'that are reassigned' do
248
          subject { described_class.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id) }
Robb Kidd's avatar
Robb Kidd committed
249 250

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

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

263 264 265 266
          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)
267
              is_expected.to have_body_text(project_merge_request_path(project, merge_request))
268 269
              is_expected.to have_html_escaped_body_text(assignee.name)
            end
Robb Kidd's avatar
Robb Kidd committed
270
          end
271 272
        end

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

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

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

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

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

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

313 314 315 316 317
          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)
318
              is_expected.to have_body_text(project_merge_request_path(project, merge_request))
319
            end
320 321 322
          end
        end

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

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

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

339 340 341 342
          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')
343
              is_expected.to have_body_text(project_merge_request_path(project, merge_request))
344
            end
345
          end
Robb Kidd's avatar
Robb Kidd committed
346 347
        end
      end
348 349
    end

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

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

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

Rémy Coutable's avatar
Rémy Coutable committed
366
    describe 'project access requested' do
367
      context 'for a project in a user namespace' do
368
        let(:project) do
369
          create(:project, :public, :access_requestable) do |project|
370 371 372 373
            project.team << [project.owner, :master, project.owner]
          end
        end

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

        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"
391
          is_expected.to have_html_escaped_body_text project.name_with_namespace
392
          is_expected.to have_body_text project_project_members_url(project)
393
          is_expected.to have_body_text project_member.human_access
394
        end
Rémy Coutable's avatar
Rémy Coutable committed
395 396
      end

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

408 409 410 411 412 413 414 415 416 417
        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"
418
          is_expected.to have_html_escaped_body_text project.name_with_namespace
419
          is_expected.to have_body_text project_project_members_url(project)
420
          is_expected.to have_body_text project_member.human_access
421
        end
422
      end
Rémy Coutable's avatar
Rémy Coutable committed
423 424 425
    end

    describe 'project access denied' do
426
      let(:project) { create(:project, :public, :access_requestable) }
Rémy Coutable's avatar
Rémy Coutable committed
427 428 429
      let(:user) { create(:user) }
      let(:project_member) do
        project.request_access(user)
430
        project.requesters.find_by(user_id: user.id)
Rémy Coutable's avatar
Rémy Coutable committed
431
      end
432
      subject { described_class.member_access_denied_email('project', project.id, user.id) }
Rémy Coutable's avatar
Rémy Coutable committed
433 434 435 436 437

      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"

438 439
      it 'contains all the useful information' do
        is_expected.to have_subject "Access to the #{project.name_with_namespace} project was denied"
440
        is_expected.to have_html_escaped_body_text project.name_with_namespace
441
        is_expected.to have_body_text project.web_url
442
      end
Rémy Coutable's avatar
Rémy Coutable committed
443 444
    end

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

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

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

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

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

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

      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"

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

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

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

      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"

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

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

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

      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"

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

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

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

Robb Kidd's avatar
Robb Kidd committed
552
      shared_examples 'a note email' do
553 554
        it_behaves_like 'it should have Gmail Actions links'

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

558 559 560 561 562
          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
Robb Kidd's avatar
Robb Kidd committed
563 564 565
        end

        it 'contains the message from the note' do
566
          is_expected.to have_html_escaped_body_text note.note
Robb Kidd's avatar
Robb Kidd committed
567
        end
568

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

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

          it 'contains a link to note author' do
579
            is_expected.to have_html_escaped_body_text note.author_name
580 581
          end
        end
Robb Kidd's avatar
Robb Kidd committed
582 583 584
      end

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

588 589 590
        before do
          allow(note).to receive(:noteable).and_return(commit)
        end
Robb Kidd's avatar
Robb Kidd committed
591

592
        subject { described_class.note_commit_email(recipient.id, note.id) }
Robb Kidd's avatar
Robb Kidd committed
593 594

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

601 602 603 604 605
        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
Robb Kidd's avatar
Robb Kidd committed
606 607 608 609
        end
      end

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

        before do
          allow(note).to receive(:noteable).and_return(merge_request)
        end
Robb Kidd's avatar
Robb Kidd committed
616

617
        subject { described_class.note_merge_request_email(recipient.id, note.id) }
Sean McGivern's avatar
Sean McGivern committed
618

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

626 627 628 629 630
        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
Robb Kidd's avatar
Robb Kidd committed
631 632 633 634
        end
      end

      describe 'on an issue' do
635
        let(:issue) { create(:issue, project: project) }
636
        let(:note_on_issue_path) { project_issue_path(project, issue, anchor: "note_#{note.id}") }
637 638 639 640

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

642
        subject { described_class.note_issue_email(recipient.id, note.id) }
Robb Kidd's avatar
Robb Kidd committed
643 644

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

651 652 653 654 655
        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
Robb Kidd's avatar
Robb Kidd committed
656 657
        end
      end
658
    end
659

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

664
      before do
665 666 667
        allow(Note).to receive(:find).with(note.id).and_return(note)
      end

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

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

Douwe Maan's avatar
Douwe Maan committed
674 675 676 677 678
          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's avatar
Douwe Maan committed
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
        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) }

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

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

        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) }
727
        let(:note_on_merge_request_path) { project_merge_request_path(project, merge_request, anchor: "note_#{note.id}") }
728 729 730 731

        before do
          allow(note).to receive(:noteable).and_return(merge_request)
        end
Douwe Maan's avatar
Douwe Maan committed
732

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

        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) }
754
        let(:note_on_issue_path) { project_issue_path(project, issue, anchor: "note_#{note.id}") }
755 756 757 758

        before do
          allow(note).to receive(:noteable).and_return(issue)
        end
Douwe Maan's avatar
Douwe Maan committed
759

760
        subject { described_class.note_issue_email(recipient.id, note.id) }
Douwe Maan's avatar
Douwe Maan committed
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781

        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') }

782
      before do
Douwe Maan's avatar
Douwe Maan committed
783 784 785 786
        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's avatar
Douwe Maan committed
787
        let(:note) { create(model, author: note_author) }
788 789

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

        it 'contains a link to the diff file' do
794
          is_expected.to have_body_text note