issuable_spec.rb 11.9 KB
Newer Older
1 2
require 'spec_helper'

D
Dmitriy Zaporozhets 已提交
3
describe Issue, "Issuable" do
4
  let(:issue) { create(:issue) }
5
  let(:user) { create(:user) }
6 7

  describe "Associations" do
8 9 10 11
    it { is_expected.to belong_to(:project) }
    it { is_expected.to belong_to(:author) }
    it { is_expected.to belong_to(:assignee) }
    it { is_expected.to have_many(:notes).dependent(:destroy) }
12
    it { is_expected.to have_many(:todos).dependent(:destroy) }
13 14 15 16 17 18 19 20 21 22

    context 'Notes' do
      let!(:note) { create(:note, noteable: issue, project: issue.project) }
      let(:scoped_issue) { Issue.includes(notes: :author).find(issue.id) }

      it 'indicates if the notes have their authors loaded' do
        expect(issue.notes).not_to be_authors_loaded
        expect(scoped_issue.notes).to be_authors_loaded
      end
    end
23 24
  end

25 26 27 28
  describe 'Included modules' do
    it { is_expected.to include_module(Awardable) }
  end

29
  describe "Validation" do
30 31 32 33
    before do
      allow(subject).to receive(:set_iid).and_return(false)
    end

34 35 36 37
    it { is_expected.to validate_presence_of(:project) }
    it { is_expected.to validate_presence_of(:iid) }
    it { is_expected.to validate_presence_of(:author) }
    it { is_expected.to validate_presence_of(:title) }
38
    it { is_expected.to validate_length_of(:title).is_at_least(0).is_at_most(255) }
39 40 41
  end

  describe "Scope" do
42 43 44
    it { expect(described_class).to respond_to(:opened) }
    it { expect(described_class).to respond_to(:closed) }
    it { expect(described_class).to respond_to(:assigned) }
45 46 47 48 49
  end

  describe ".search" do
    let!(:searchable_issue) { create(:issue, title: "Searchable issue") }

Y
Yorick Peterse 已提交
50
    it 'returns notes with a matching title' do
51 52 53 54 55
      expect(described_class.search(searchable_issue.title)).
        to eq([searchable_issue])
    end

    it 'returns notes with a partially matching title' do
56
      expect(described_class.search('able')).to eq([searchable_issue])
57
    end
58 59 60 61 62 63 64 65 66 67 68 69

    it 'returns notes with a matching title regardless of the casing' do
      expect(described_class.search(searchable_issue.title.upcase)).
        to eq([searchable_issue])
    end
  end

  describe ".full_search" do
    let!(:searchable_issue) do
      create(:issue, title: "Searchable issue", description: 'kittens')
    end

Y
Yorick Peterse 已提交
70
    it 'returns notes with a matching title' do
71 72 73 74 75 76 77 78 79 80 81 82 83
      expect(described_class.full_search(searchable_issue.title)).
        to eq([searchable_issue])
    end

    it 'returns notes with a partially matching title' do
      expect(described_class.full_search('able')).to eq([searchable_issue])
    end

    it 'returns notes with a matching title regardless of the casing' do
      expect(described_class.full_search(searchable_issue.title.upcase)).
        to eq([searchable_issue])
    end

Y
Yorick Peterse 已提交
84
    it 'returns notes with a matching description' do
85 86 87 88 89 90 91 92 93 94 95 96 97
      expect(described_class.full_search(searchable_issue.description)).
        to eq([searchable_issue])
    end

    it 'returns notes with a partially matching description' do
      expect(described_class.full_search(searchable_issue.description)).
        to eq([searchable_issue])
    end

    it 'returns notes with a matching description regardless of the casing' do
      expect(described_class.full_search(searchable_issue.description.upcase)).
        to eq([searchable_issue])
    end
98 99
  end

100 101 102 103 104
  describe '.to_ability_name' do
    it { expect(Issue.to_ability_name).to eq("issue") }
    it { expect(MergeRequest.to_ability_name).to eq("merge_request") }
  end

105 106 107
  describe "#today?" do
    it "returns true when created today" do
      # Avoid timezone differences and just return exactly what we want
108 109
      allow(Date).to receive(:today).and_return(issue.created_at.to_date)
      expect(issue.today?).to be_truthy
110 111 112
    end

    it "returns false when not created today" do
113 114
      allow(Date).to receive(:today).and_return(Date.yesterday)
      expect(issue.today?).to be_falsey
115 116 117 118 119
    end
  end

  describe "#new?" do
    it "returns true when created today and record hasn't been updated" do
120 121
      allow(issue).to receive(:today?).and_return(true)
      expect(issue.new?).to be_truthy
122 123 124
    end

    it "returns false when not created today" do
125 126
      allow(issue).to receive(:today?).and_return(false)
      expect(issue.new?).to be_falsey
127 128 129
    end

    it "returns false when record has been updated" do
130
      allow(issue).to receive(:today?).and_return(true)
131
      issue.touch
132
      expect(issue.new?).to be_falsey
133 134
    end
  end
135

136
  describe "#sort" do
137 138 139
    let(:project) { build_stubbed(:empty_project) }

    context "by milestone due date" do
F
Felipe Artur 已提交
140 141 142 143
      # Correct order is:
      # Issues/MRs with milestones ordered by date
      # Issues/MRs with milestones without dates
      # Issues/MRs without milestones
144

145 146 147 148 149 150 151 152 153 154 155
      let!(:issue) { create(:issue, project: project) }
      let!(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
      let!(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
      let!(:issue1) { create(:issue, project: project, milestone: early_milestone) }
      let!(:issue2) { create(:issue, project: project, milestone: late_milestone) }
      let!(:issue3) { create(:issue, project: project) }

      it "sorts desc" do
        issues = project.issues.sort('milestone_due_desc')
        expect(issues).to match_array([issue2, issue1, issue, issue3])
      end
156

157 158 159
      it "sorts asc" do
        issues = project.issues.sort('milestone_due_asc')
        expect(issues).to match_array([issue1, issue2, issue, issue3])
160 161
      end
    end
162 163 164 165 166 167 168 169 170 171 172 173 174 175

    context 'when all of the results are level on the sort key' do
      let!(:issues) do
        10.times { create(:issue, project: project) }
      end

      it 'has no duplicates across pages' do
        sorted_issue_ids = 1.upto(10).map do |i|
          project.issues.sort('milestone_due_desc').page(i).per(1).first.id
        end

        expect(sorted_issue_ids).to eq(sorted_issue_ids.uniq)
      end
    end
176 177
  end

178
  describe '#subscribed?' do
179 180
    let(:project) { issue.project }

181 182 183 184 185 186 187 188
    context 'user is not a participant in the issue' do
      before { allow(issue).to receive(:participants).with(user).and_return([]) }

      it 'returns false when no subcription exists' do
        expect(issue.subscribed?(user)).to be_falsey
      end

      it 'returns true when a subcription exists and subscribed is true' do
189
        issue.subscriptions.create(user: user, project: project, subscribed: true)
190 191 192 193 194

        expect(issue.subscribed?(user)).to be_truthy
      end

      it 'returns false when a subcription exists and subscribed is false' do
195
        issue.subscriptions.create(user: user, project: project, subscribed: false)
196 197 198 199 200 201 202 203 204 205 206 207 208

        expect(issue.subscribed?(user)).to be_falsey
      end
    end

    context 'user is a participant in the issue' do
      before { allow(issue).to receive(:participants).with(user).and_return([user]) }

      it 'returns false when no subcription exists' do
        expect(issue.subscribed?(user)).to be_truthy
      end

      it 'returns true when a subcription exists and subscribed is true' do
209
        issue.subscriptions.create(user: user, project: project, subscribed: true)
210 211 212 213 214

        expect(issue.subscribed?(user)).to be_truthy
      end

      it 'returns false when a subcription exists and subscribed is false' do
215
        issue.subscriptions.create(user: user, project: project, subscribed: false)
216 217 218 219 220 221

        expect(issue.subscribed?(user)).to be_falsey
      end
    end
  end

222
  describe "#to_hook_data" do
223 224 225
    let(:data) { issue.to_hook_data(user) }
    let(:project) { issue.project }

226
    it "returns correct hook data" do
227 228 229
      expect(data[:object_kind]).to eq("issue")
      expect(data[:user]).to eq(user.hook_attrs)
      expect(data[:object_attributes]).to eq(issue.hook_attrs)
230
      expect(data).not_to have_key(:assignee)
231 232 233 234 235 236
    end

    context "issue is assigned" do
      before { issue.update_attribute(:assignee, user) }

      it "returns correct hook data" do
237 238
        expect(data[:object_attributes]['assignee_id']).to eq(user.id)
        expect(data[:assignee]).to eq(user.hook_attrs)
239
      end
240
    end
241 242 243

    include_examples 'project hook data'
    include_examples 'deprecated repository hook data'
244
  end
245 246 247 248 249 250 251

  describe '#card_attributes' do
    it 'includes the author name' do
      allow(issue).to receive(:author).and_return(double(name: 'Robert'))
      allow(issue).to receive(:assignee).and_return(nil)

      expect(issue.card_attributes).
D
Douwe Maan 已提交
252
        to eq({ 'Author' => 'Robert', 'Assignee' => nil })
253 254 255 256 257 258 259
    end

    it 'includes the assignee name' do
      allow(issue).to receive(:author).and_return(double(name: 'Robert'))
      allow(issue).to receive(:assignee).and_return(double(name: 'Douwe'))

      expect(issue.card_attributes).
D
Douwe Maan 已提交
260
        to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
261 262
    end
  end
263

264 265 266 267 268 269 270 271 272 273 274 275 276 277
  describe '#labels_array' do
    let(:project) { create(:project) }
    let(:bug) { create(:label, project: project, title: 'bug') }
    let(:issue) { create(:issue, project: project) }

    before(:each) do
      issue.labels << bug
    end

    it 'loads the association and returns it as an array' do
      expect(issue.reload.labels_array).to eq([bug])
    end
  end

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
  describe '#user_notes_count' do
    let(:project) { create(:project) }
    let(:issue1) { create(:issue, project: project) }
    let(:issue2) { create(:issue, project: project) }

    before do
      create_list(:note, 3, noteable: issue1, project: project)
      create_list(:note, 6, noteable: issue2, project: project)
    end

    it 'counts the user notes' do
      expect(issue1.user_notes_count).to be(3)
      expect(issue2.user_notes_count).to be(6)
    end
  end

294
  describe "votes" do
295 296
    let(:project) { issue.project }

297
    before do
Z
Zeger-Jan van de Weg 已提交
298 299
      create(:award_emoji, :upvote, awardable: issue)
      create(:award_emoji, :downvote, awardable: issue)
300 301 302 303 304 305 306
    end

    it "returns correct values" do
      expect(issue.upvotes).to eq(1)
      expect(issue.downvotes).to eq(1)
    end
  end
307

308 309 310 311 312 313 314 315 316 317 318 319 320 321
  describe '.order_labels_priority' do
    let(:label_1) { create(:label, title: 'label_1', project: issue.project, priority: 1) }
    let(:label_2) { create(:label, title: 'label_2', project: issue.project, priority: 2) }

    subject { Issue.order_labels_priority(excluded_labels: ['label_1']).first.highest_priority }

    before do
      issue.labels << label_1
      issue.labels << label_2
    end

    it { is_expected.to eq(2) }
  end

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
  describe ".with_label" do
    let(:project) { create(:project, :public) }
    let(:bug) { create(:label, project: project, title: 'bug') }
    let(:feature) { create(:label, project: project, title: 'feature') }
    let(:enhancement) { create(:label, project: project, title: 'enhancement') }
    let(:issue1) { create(:issue, title: "Bugfix1", project: project) }
    let(:issue2) { create(:issue, title: "Bugfix2", project: project) }
    let(:issue3) { create(:issue, title: "Feature1", project: project) }

    before(:each) do
      issue1.labels << bug
      issue1.labels << feature
      issue2.labels << bug
      issue2.labels << enhancement
      issue3.labels << feature
    end

    it 'finds the correct issue containing just enhancement label' do
      expect(Issue.with_label(enhancement.title)).to match_array([issue2])
    end

    it 'finds the correct issues containing the same label' do
      expect(Issue.with_label(bug.title)).to match_array([issue1, issue2])
    end

    it 'finds the correct issues containing only both labels' do
      expect(Issue.with_label([bug.title, enhancement.title])).to match_array([issue2])
    end
  end
Y
Yorick Peterse 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

  describe '#assignee_or_author?' do
    let(:user) { build(:user, id: 1) }
    let(:issue) { build(:issue) }

    it 'returns true for a user that is assigned to an issue' do
      issue.assignee = user

      expect(issue.assignee_or_author?(user)).to eq(true)
    end

    it 'returns true for a user that is the author of an issue' do
      issue.author = user

      expect(issue.assignee_or_author?(user)).to eq(true)
    end

    it 'returns false for a user that is not the assignee or author' do
      expect(issue.assignee_or_author?(user)).to eq(false)
    end
  end
372
end