issues_spec.rb 13.3 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

67 68 69 70 71 72 73 74 75 76 77 78
  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
79
  describe 'Filter issue' do
80 81
    before do
      ['foobar', 'barbaz', 'gitlab'].each do |title|
Riyad Preukschas's avatar
Riyad Preukschas committed
82 83 84 85 86
        create(:issue,
               author: @user,
               assignee: @user,
               project: project,
               title: title)
87 88
      end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
89
      @issue = Issue.find_by(title: 'foobar')
90 91 92
      @issue.milestone = create(:milestone, project: project)
      @issue.assignee = nil
      @issue.save
93 94
    end

95
    let(:issue) { @issue }
Riyad Preukschas's avatar
Riyad Preukschas committed
96

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

100 101 102
      expect(page).to have_content 'foobar'
      expect(page).not_to have_content 'barbaz'
      expect(page).not_to have_content 'gitlab'
103 104
    end

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

108 109 110
      expect(page).not_to have_content 'foobar'
      expect(page).to have_content 'barbaz'
      expect(page).to have_content 'gitlab'
111
    end
112
  end
113 114

  describe 'filter issue' do
Rémy Coutable's avatar
Rémy Coutable committed
115
    titles = %w[foo bar baz]
116
    titles.each_with_index do |title, index|
Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
117 118 119 120 121
      let!(title.to_sym) do
        create(:issue, title: title,
                       project: project,
                       created_at: Time.now - (index * 60))
      end
122
    end
123 124
    let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
    let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
125 126

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

129 130
      expect(first_issue).to include('baz')
      expect(last_issue).to include('foo')
131 132 133
    end

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

136 137
      expect(first_issue).to include('foo')
      expect(last_issue).to include('baz')
138 139 140 141 142
    end

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

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
145
      expect(first_issue).to include('baz')
146 147 148 149 150
    end

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

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
153
      expect(first_issue).to include('baz')
154 155
    end

156
    describe 'sorting by due date' do
157
      before do
Rémy Coutable's avatar
Rémy Coutable committed
158 159
        foo.update(due_date: 1.day.from_now)
        bar.update(due_date: 6.days.from_now)
160 161 162 163
      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
164

165 166 167 168 169
        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
170

171 172 173 174 175
        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
176

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

179 180
        expect(first_issue).to include('foo')
      end
181 182 183 184 185 186 187 188 189 190 191 192 193

      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
194 195 196
    end

    describe 'filtering by due date' do
Rémy Coutable's avatar
Rémy Coutable committed
197 198 199
      before do
        foo.update(due_date: 1.day.from_now)
        bar.update(due_date: 6.days.from_now)
200 201 202
      end

      it 'filters by none' do
203
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::NoDueDate.name)
Rémy Coutable's avatar
Rémy Coutable committed
204 205 206 207

        expect(page).not_to have_content('foo')
        expect(page).not_to have_content('bar')
        expect(page).to have_content('baz')
208 209 210
      end

      it 'filters by any' do
211
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::AnyDueDate.name)
Rémy Coutable's avatar
Rémy Coutable committed
212 213 214 215

        expect(page).to have_content('foo')
        expect(page).to have_content('bar')
        expect(page).to have_content('baz')
216 217
      end

218 219 220 221
      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
222

223
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisWeek.name)
Rémy Coutable's avatar
Rémy Coutable committed
224 225 226 227

        expect(page).to have_content('foo')
        expect(page).to have_content('bar')
        expect(page).not_to have_content('baz')
228 229
      end

230 231 232 233
      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
234

235
        visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisMonth.name)
Rémy Coutable's avatar
Rémy Coutable committed
236 237 238 239

        expect(page).to have_content('foo')
        expect(page).to have_content('bar')
        expect(page).not_to have_content('baz')
240 241
      end

242 243 244 245 246
      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
247 248 249 250 251 252
        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
253 254
    end

255
    describe 'sorting by milestone' do
Rémy Coutable's avatar
Rémy Coutable committed
256
      before do
257 258 259 260 261 262 263
        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
264
        visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_soon)
265

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
266
        expect(first_issue).to include('foo')
267
        expect(last_issue).to include('baz')
268 269 270
      end

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

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
273
        expect(first_issue).to include('bar')
274
        expect(last_issue).to include('baz')
275 276 277 278 279 280
      end
    end

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

Rémy Coutable's avatar
Rémy Coutable committed
281
      before do
282 283 284 285 286 287 288
        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
289 290 291
        visit namespace_project_issues_path(project.namespace, project,
                                            sort: sort_value_oldest_created,
                                            assignee_id: user2.id)
292

293 294
        expect(first_issue).to include('foo')
        expect(last_issue).to include('bar')
295
        expect(page).not_to have_content 'baz'
296 297 298
      end
    end
  end
299

300
  describe 'update assignee from issue#show' do
301
    let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
302

303
    context 'by authorized user' do
304

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

308 309 310 311 312 313 314 315 316 317 318
        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
319

320
        expect(issue.reload.assignee).to be_nil
321
      end
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342

      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
        
        page.within '.dropdown-menu-user' do
          click_link @user.name
        end

        page.within('.assignee') do
          expect(page).to have_content @user.name
        end
      end
343 344 345
    end

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

347
      let(:guest) { create(:user) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
348

Rémy Coutable's avatar
Rémy Coutable committed
349
      before do
350 351 352
        project.team << [[guest], :guest]
      end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
353
      it 'shows assignee text', js: true do
354 355 356
        logout
        login_with guest

Vinnie Okada's avatar
Vinnie Okada committed
357
        visit namespace_project_issue_path(project.namespace, project, issue)
358
        expect(page).to have_content issue.assignee.name
359 360 361 362 363 364 365 366 367 368 369
      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


370 371
      it 'allows user to select unassigned', js: true do
        visit namespace_project_issue_path(project.namespace, project, issue)
372

373 374 375
        page.within('.milestone') do
          expect(page).to have_content "None"
        end
376

377 378 379 380
        find('.block.milestone .edit-link').click
        sleep 2 # wait for ajax stuff to complete
        first('.dropdown-content li').click
        sleep 2
381
        page.within('.milestone') do
382
          expect(page).to have_content 'None'
383 384
        end

385
        expect(issue.reload.milestone).to be_nil
386 387 388 389 390
      end
    end

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

Rémy Coutable's avatar
Rémy Coutable committed
392
      before do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
393
        project.team << [guest, :guest]
394 395 396 397
        issue.milestone = milestone
        issue.save
      end

Jeroen van Baarsen's avatar
Jeroen van Baarsen committed
398
      it 'shows milestone text', js: true do
399 400 401
        logout
        login_with guest

Vinnie Okada's avatar
Vinnie Okada committed
402
        visit namespace_project_issue_path(project.namespace, project, issue)
403
        expect(page).to have_content milestone.title
404 405
      end
    end
406 407 408 409

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

Rémy Coutable's avatar
Rémy Coutable committed
410
      before do
411 412 413 414
        issue.assignee = user2
        issue.save
      end
    end
415 416
  end

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
  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

434
  def first_issue
Douwe Maan's avatar
Douwe Maan committed
435
    page.all('ul.issues-list > li').first.text
436 437 438
  end

  def last_issue
Douwe Maan's avatar
Douwe Maan committed
439
    page.all('ul.issues-list > li').last.text
440
  end
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461

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