issues_spec.rb 14.9 KB
Newer Older
gitlabhq's avatar
gitlabhq committed
1 2
require 'spec_helper'

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
3
describe 'Issues', feature: true do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
4 5
  include SortingHelper

6
  let(:project) { create(:project) }
gitlabhq's avatar
gitlabhq committed
7

Nihad Abbasov's avatar
Nihad Abbasov committed
8
  before do
gitlabhq's avatar
gitlabhq committed
9
    login_as :user
Riyad Preukschas's avatar
Riyad Preukschas committed
10
    user2 = create(:user)
gitlabhq's avatar
fixes  
gitlabhq committed
11

12
    project.team << [[@user, user2], :developer]
gitlabhq's avatar
gitlabhq committed
13 14
  end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
15
  describe 'Edit issue' do
Riyad Preukschas's avatar
Riyad Preukschas committed
16 17 18 19 20 21 22
    let!(:issue) do
      create(:issue,
             author: @user,
             assignee: @user,
             project: project)
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
23
    before do
24
      visit edit_namespace_project_issue_path(project.namespace, project, issue)
Phil Hughes's avatar
Phil Hughes committed
25
      click_button "Go full screen"
gitlabhq's avatar
gitlabhq committed
26 27
    end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
28
    it 'should open new issue popup' do
29
      expect(page).to have_content("Issue ##{issue.iid}")
gitlabhq's avatar
gitlabhq committed
30 31
    end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
32
    describe 'fill in' do
gitlabhq's avatar
gitlabhq committed
33
      before do
Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
34 35
        fill_in 'issue_title', with: 'bug 345'
        fill_in 'issue_description', with: 'bug description'
gitlabhq's avatar
gitlabhq committed
36 37
      end
    end
38 39
  end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
40
  describe 'Editing issue assignee' do
41 42 43 44 45 46 47
    let!(:issue) do
      create(:issue,
             author: @user,
             assignee: @user,
             project: project)
    end

48
    it 'allows user to select unassigned', js: true do
Vinnie Okada's avatar
Vinnie Okada committed
49
      visit edit_namespace_project_issue_path(project.namespace, project, issue)
50

51
      expect(page).to have_content "Assignee #{@user.name}"
52

53
      first('#s2id_issue_assignee_id').click
54
      sleep 2 # wait for ajax stuff to complete
55
      first('.user-result').click
56

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
57
      click_button 'Save changes'
58

59
      page.within('.assignee') do
60
        expect(page).to have_content 'No assignee - assign yourself'
61 62
      end

63
      expect(issue.reload.assignee).to be_nil
64
    end
gitlabhq's avatar
gitlabhq committed
65
  end
Adam Leonard's avatar
Adam Leonard committed
66

Phil Hughes's avatar
Phil Hughes committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
  describe 'due date', js: true do
    context 'on new form' do
      before do
        visit new_namespace_project_issue_path(project.namespace, project)
      end

      it 'should save with due date' do
        date = Date.today.at_beginning_of_month

        fill_in 'issue_title', with: 'bug 345'
        fill_in 'issue_description', with: 'bug description'

        page.within '.datepicker' do
          click_link date.day
        end

Phil Hughes's avatar
Phil Hughes committed
83
        expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
Phil Hughes's avatar
Phil Hughes committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

        click_button 'Submit issue'

        page.within '.issuable-sidebar' do
          expect(page).to have_content date.to_s(:medium)
        end
      end
    end

    context 'on edit form' do
      let(:issue) { create(:issue, author: @user,project: project, due_date: Date.today.at_beginning_of_month.to_s) }

      before do
        visit edit_namespace_project_issue_path(project.namespace, project, issue)
      end

      it 'should save with due date' do
        date = Date.today.at_beginning_of_month

Phil Hughes's avatar
Phil Hughes committed
103
        expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
Phil Hughes's avatar
Phil Hughes committed
104 105 106 107 108 109 110 111 112 113

        date = date.tomorrow

        fill_in 'issue_title', with: 'bug 345'
        fill_in 'issue_description', with: 'bug description'

        page.within '.datepicker' do
          click_link date.day
        end

Phil Hughes's avatar
Phil Hughes committed
114
        expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
Phil Hughes's avatar
Phil Hughes committed
115 116 117 118 119 120 121 122 123 124

        click_button 'Save changes'

        page.within '.issuable-sidebar' do
          expect(page).to have_content date.to_s(:medium)
        end
      end
    end
  end

125 126 127 128 129 130 131 132 133 134 135 136
  describe 'Issue info' do
    it 'excludes award_emoji from comment count' do
      issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar')
      create(:upvote_note, noteable: issue)

      visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)

      expect(page).to have_content 'foobar'
      expect(page.all('.issue-no-comments').first.text).to eq "0"
    end
  end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
137
  describe 'Filter issue' do
138 139
    before do
      ['foobar', 'barbaz', 'gitlab'].each do |title|
Riyad Preukschas's avatar
Riyad Preukschas committed
140 141 142 143 144
        create(:issue,
               author: @user,
               assignee: @user,
               project: project,
               title: title)
145 146
      end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
147
      @issue = Issue.find_by(title: 'foobar')
148 149 150
      @issue.milestone = create(:milestone, project: project)
      @issue.assignee = nil
      @issue.save
151 152
    end

153
    let(:issue) { @issue }
Riyad Preukschas's avatar
Riyad Preukschas committed
154

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
155
    it 'should allow filtering by issues with no specified assignee' do
156
      visit namespace_project_issues_path(project.namespace, project, assignee_id: IssuableFinder::NONE)
157

158 159 160
      expect(page).to have_content 'foobar'
      expect(page).not_to have_content 'barbaz'
      expect(page).not_to have_content 'gitlab'
161 162
    end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
163
    it 'should allow filtering by a specified assignee' do
Vinnie Okada's avatar
Vinnie Okada committed
164
      visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)
165

166 167 168
      expect(page).not_to have_content 'foobar'
      expect(page).to have_content 'barbaz'
      expect(page).to have_content 'gitlab'
169
    end
170
  end
171 172

  describe 'filter issue' do
Rémy Coutable's avatar
Rémy Coutable committed
173
    titles = %w[foo bar baz]
174
    titles.each_with_index do |title, index|
Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
175 176 177 178 179
      let!(title.to_sym) do
        create(:issue, title: title,
                       project: project,
                       created_at: Time.now - (index * 60))
      end
180
    end
181 182
    let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
    let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
183 184

    it 'sorts by newest' do
Vinnie Okada's avatar
Vinnie Okada committed
185
      visit namespace_project_issues_path(project.namespace, project, sort: sort_value_recently_created)
186

187 188
      expect(first_issue).to include('baz')
      expect(last_issue).to include('foo')
189 190 191
    end

    it 'sorts by oldest' do
Vinnie Okada's avatar
Vinnie Okada committed
192
      visit namespace_project_issues_path(project.namespace, project, sort: sort_value_oldest_created)
193

194 195
      expect(first_issue).to include('foo')
      expect(last_issue).to include('baz')
196 197 198 199 200
    end

    it 'sorts by most recently updated' do
      baz.updated_at = Time.now + 100
      baz.save
Vinnie Okada's avatar
Vinnie Okada committed
201
      visit namespace_project_issues_path(project.namespace, project, sort: sort_value_recently_updated)
202

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
203
      expect(first_issue).to include('baz')
204 205 206 207 208
    end

    it 'sorts by least recently updated' do
      baz.updated_at = Time.now - 100
      baz.save
Vinnie Okada's avatar
Vinnie Okada committed
209
      visit namespace_project_issues_path(project.namespace, project, sort: sort_value_oldest_updated)
210

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
211
      expect(first_issue).to include('baz')
212 213
    end

214
    describe 'sorting by due date' do
215
      before do
Rémy Coutable's avatar
Rémy Coutable committed
216 217
        foo.update(due_date: 1.day.from_now)
        bar.update(due_date: 6.days.from_now)
218 219 220 221
      end

      it 'sorts by recently due date' do
        visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_soon)
Rémy Coutable's avatar
Rémy Coutable committed
222

223 224 225 226 227
        expect(first_issue).to include('foo')
      end

      it 'sorts by least recently due date' do
        visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_later)
Rémy Coutable's avatar
Rémy Coutable committed
228

229 230 231 232 233
        expect(first_issue).to include('bar')
      end

      it 'sorts by least recently due date by excluding nil due dates' do
        bar.update(due_date: nil)
Rémy Coutable's avatar
Rémy Coutable committed
234

235
        visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_later)
Rémy Coutable's avatar
Rémy Coutable committed
236

237 238
        expect(first_issue).to include('foo')
      end
239 240 241 242 243 244 245 246 247 248 249 250 251

      context 'with a filter on labels' do
        let(:label) { create(:label, project: project) }
        before { create(:label_link, label: label, target: foo) }

        it 'sorts by least recently due date by excluding nil due dates' do
          bar.update(due_date: nil)

          visit namespace_project_issues_path(project.namespace, project, label_names: [label.name], sort: sort_value_due_date_later)

          expect(first_issue).to include('foo')
        end
      end
252 253 254
    end

    describe 'filtering by due date' do
Rémy Coutable's avatar
Rémy Coutable committed
255 256 257
      before do
        foo.update(due_date: 1.day.from_now)
        bar.update(due_date: 6.days.from_now)
258 259 260
      end

      it 'filters by none' do
261
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::NoDueDate.name)
Rémy Coutable's avatar
Rémy Coutable committed
262 263 264 265

        expect(page).not_to have_content('foo')
        expect(page).not_to have_content('bar')
        expect(page).to have_content('baz')
266 267 268
      end

      it 'filters by any' do
269
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::AnyDueDate.name)
Rémy Coutable's avatar
Rémy Coutable committed
270 271 272 273

        expect(page).to have_content('foo')
        expect(page).to have_content('bar')
        expect(page).to have_content('baz')
274 275
      end

276 277 278 279
      it 'filters by due this week' do
        foo.update(due_date: Date.today.beginning_of_week + 2.days)
        bar.update(due_date: Date.today.end_of_week)
        baz.update(due_date: Date.today - 8.days)
Rémy Coutable's avatar
Rémy Coutable committed
280

281
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisWeek.name)
Rémy Coutable's avatar
Rémy Coutable committed
282 283 284 285

        expect(page).to have_content('foo')
        expect(page).to have_content('bar')
        expect(page).not_to have_content('baz')
286 287
      end

288 289 290 291
      it 'filters by due this month' do
        foo.update(due_date: Date.today.beginning_of_month + 2.days)
        bar.update(due_date: Date.today.end_of_month)
        baz.update(due_date: Date.today - 50.days)
Rémy Coutable's avatar
Rémy Coutable committed
292

293
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisMonth.name)
Rémy Coutable's avatar
Rémy Coutable committed
294 295 296 297

        expect(page).to have_content('foo')
        expect(page).to have_content('bar')
        expect(page).not_to have_content('baz')
298 299
      end

300 301 302 303 304
      it 'filters by overdue' do
        foo.update(due_date: Date.today + 2.days)
        bar.update(due_date: Date.today + 20.days)
        baz.update(due_date: Date.yesterday)

Rémy Coutable's avatar
Rémy Coutable committed
305 306 307 308 309 310
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::Overdue.name)

        expect(page).not_to have_content('foo')
        expect(page).not_to have_content('bar')
        expect(page).to have_content('baz')
      end
311 312
    end

313
    describe 'sorting by milestone' do
Rémy Coutable's avatar
Rémy Coutable committed
314
      before do
315 316 317 318 319 320 321
        foo.milestone = newer_due_milestone
        foo.save
        bar.milestone = later_due_milestone
        bar.save
      end

      it 'sorts by recently due milestone' do
Vinnie Okada's avatar
Vinnie Okada committed
322
        visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_soon)
323

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
324
        expect(first_issue).to include('foo')
325
        expect(last_issue).to include('baz')
326 327 328
      end

      it 'sorts by least recently due milestone' do
Vinnie Okada's avatar
Vinnie Okada committed
329
        visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_later)
330

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
331
        expect(first_issue).to include('bar')
332
        expect(last_issue).to include('baz')
333 334 335 336 337 338
      end
    end

    describe 'combine filter and sort' do
      let(:user2) { create(:user) }

Rémy Coutable's avatar
Rémy Coutable committed
339
      before do
340 341 342 343 344 345 346
        foo.assignee = user2
        foo.save
        bar.assignee = user2
        bar.save
      end

      it 'sorts with a filter applied' do
Vinnie Okada's avatar
Vinnie Okada committed
347 348 349
        visit namespace_project_issues_path(project.namespace, project,
                                            sort: sort_value_oldest_created,
                                            assignee_id: user2.id)
350

351 352
        expect(first_issue).to include('foo')
        expect(last_issue).to include('bar')
353
        expect(page).not_to have_content 'baz'
354 355 356
      end
    end
  end
357

358
  describe 'update assignee from issue#show' do
359
    let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
360

361
    context 'by authorized user' do
362

363
      it 'allows user to select unassigned', js: true do
Vinnie Okada's avatar
Vinnie Okada committed
364
        visit namespace_project_issue_path(project.namespace, project, issue)
365

366 367 368 369 370 371 372 373 374 375 376
        page.within('.assignee') do
          expect(page).to have_content "#{@user.name}"
        end

        find('.block.assignee .edit-link').click
        sleep 2 # wait for ajax stuff to complete
        first('.dropdown-menu-user-link').click
        sleep 2
        page.within('.assignee') do
          expect(page).to have_content 'No assignee'
        end
377

378
        expect(issue.reload.assignee).to be_nil
379
      end
380 381 382 383 384 385 386 387 388 389 390 391

      it 'allows user to select an assignee', js: true do
        issue2 = create(:issue, project: project, author: @user)
        visit namespace_project_issue_path(project.namespace, project, issue2)

        page.within('.assignee') do
          expect(page).to have_content "No assignee"
        end

        page.within '.assignee' do
          click_link 'Edit'
        end
Phil Hughes's avatar
Phil Hughes committed
392

393 394 395 396 397 398 399 400
        page.within '.dropdown-menu-user' do
          click_link @user.name
        end

        page.within('.assignee') do
          expect(page).to have_content @user.name
        end
      end
401 402 403
    end

    context 'by unauthorized user' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
404

405
      let(:guest) { create(:user) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
406

Rémy Coutable's avatar
Rémy Coutable committed
407
      before do
408 409 410
        project.team << [[guest], :guest]
      end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
411
      it 'shows assignee text', js: true do
412 413 414
        logout
        login_with guest

Vinnie Okada's avatar
Vinnie Okada committed
415
        visit namespace_project_issue_path(project.namespace, project, issue)
416
        expect(page).to have_content issue.assignee.name
417 418 419 420 421 422 423 424 425 426 427
      end
    end
  end

  describe 'update milestone from issue#show' do
    let!(:issue) { create(:issue, project: project, author: @user) }
    let!(:milestone) { create(:milestone, project: project) }

    context 'by authorized user' do


428 429
      it 'allows user to select unassigned', js: true do
        visit namespace_project_issue_path(project.namespace, project, issue)
430

431 432 433
        page.within('.milestone') do
          expect(page).to have_content "None"
        end
434

435 436 437 438
        find('.block.milestone .edit-link').click
        sleep 2 # wait for ajax stuff to complete
        first('.dropdown-content li').click
        sleep 2
439
        page.within('.milestone') do
440
          expect(page).to have_content 'None'
441 442
        end

443
        expect(issue.reload.milestone).to be_nil
444 445 446 447 448
      end
    end

    context 'by unauthorized user' do
      let(:guest) { create(:user) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
449

Rémy Coutable's avatar
Rémy Coutable committed
450
      before do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
451
        project.team << [guest, :guest]
452 453 454 455
        issue.milestone = milestone
        issue.save
      end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
456
      it 'shows milestone text', js: true do
457 458 459
        logout
        login_with guest

Vinnie Okada's avatar
Vinnie Okada committed
460
        visit namespace_project_issue_path(project.namespace, project, issue)
461
        expect(page).to have_content milestone.title
462 463
      end
    end
464 465 466 467

    describe 'removing assignee' do
      let(:user2) { create(:user) }

Rémy Coutable's avatar
Rémy Coutable committed
468
      before do
469 470 471 472
        issue.assignee = user2
        issue.save
      end
    end
473 474
  end

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
  describe 'new issue' do
    context 'dropzone upload file', js: true do
      before do
        visit new_namespace_project_issue_path(project.namespace, project)
      end

      it 'should upload file when dragging into textarea' do
        drop_in_dropzone test_image_file

        # Wait for the file to upload
        sleep 1

        expect(page.find_field("issue_description").value).to have_content 'banana_sample'
      end
    end
  end

492
  def first_issue
Douwe Maan's avatar
Douwe Maan committed
493
    page.all('ul.issues-list > li').first.text
494 495 496
  end

  def last_issue
Douwe Maan's avatar
Douwe Maan committed
497
    page.all('ul.issues-list > li').last.text
498
  end
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

  def drop_in_dropzone(file_path)
    # Generate a fake input selector
    page.execute_script <<-JS
      var fakeFileInput = window.$('<input/>').attr(
        {id: 'fakeFileInput', type: 'file'}
      ).appendTo('body');
    JS
    # Attach the file to the fake input selector with Capybara
    attach_file("fakeFileInput", file_path)
    # Add the file to a fileList array and trigger the fake drop event
    page.execute_script <<-JS
      var fileList = [$('#fakeFileInput')[0].files[0]];
      var e = jQuery.Event('drop', { dataTransfer : { files : fileList } });
      $('.div-dropzone')[0].dropzone.listeners[0].events.drop(e);
    JS
  end

  def test_image_file
    File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
520
end