issuable_spec.rb 10.2 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 100 101 102
  end

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

    it "returns false when not created today" do
108 109
      allow(Date).to receive(:today).and_return(Date.yesterday)
      expect(issue.today?).to be_falsey
110 111 112 113 114
    end
  end

  describe "#new?" do
    it "returns true when created today and record hasn't been updated" do
115 116
      allow(issue).to receive(:today?).and_return(true)
      expect(issue.new?).to be_truthy
117 118 119
    end

    it "returns false when not created today" do
120 121
      allow(issue).to receive(:today?).and_return(false)
      expect(issue.new?).to be_falsey
122 123 124
    end

    it "returns false when record has been updated" do
125
      allow(issue).to receive(:today?).and_return(true)
126
      issue.touch
127
      expect(issue.new?).to be_falsey
128 129
    end
  end
130

131
  describe "#sort" do
132 133 134
    let(:project) { build_stubbed(:empty_project) }

    context "by milestone due date" do
F
Felipe Artur 已提交
135 136 137 138
      # Correct order is:
      # Issues/MRs with milestones ordered by date
      # Issues/MRs with milestones without dates
      # Issues/MRs without milestones
139

140 141 142 143 144 145 146 147 148 149 150
      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
151

152 153 154
      it "sorts asc" do
        issues = project.issues.sort('milestone_due_asc')
        expect(issues).to match_array([issue1, issue2, issue, issue3])
155 156 157 158 159
      end
    end
  end


160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
  describe '#subscribed?' do
    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
        issue.subscriptions.create(user: user, subscribed: true)

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

      it 'returns false when a subcription exists and subscribed is false' do
        issue.subscriptions.create(user: user, subscribed: false)

        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
        issue.subscriptions.create(user: user, subscribed: true)

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

      it 'returns false when a subcription exists and subscribed is false' do
        issue.subscriptions.create(user: user, subscribed: false)

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

202
  describe "#to_hook_data" do
203 204 205
    let(:data) { issue.to_hook_data(user) }
    let(:project) { issue.project }

206
    it "returns correct hook data" do
207 208 209
      expect(data[:object_kind]).to eq("issue")
      expect(data[:user]).to eq(user.hook_attrs)
      expect(data[:object_attributes]).to eq(issue.hook_attrs)
210
      expect(data).not_to have_key(:assignee)
211 212 213 214 215 216
    end

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

      it "returns correct hook data" do
217 218
        expect(data[:object_attributes]['assignee_id']).to eq(user.id)
        expect(data[:assignee]).to eq(user.hook_attrs)
219
      end
220
    end
221 222 223

    include_examples 'project hook data'
    include_examples 'deprecated repository hook data'
224
  end
225 226 227 228 229 230 231

  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 已提交
232
        to eq({ 'Author' => 'Robert', 'Assignee' => nil })
233 234 235 236 237 238 239
    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 已提交
240
        to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
241 242
    end
  end
243

244 245 246 247 248 249 250 251 252 253 254 255 256 257
  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

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
  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

274
  describe "votes" do
275 276
    let(:project) { issue.project }

277
    before do
Z
Zeger-Jan van de Weg 已提交
278 279
      create(:award_emoji, :upvote, awardable: issue)
      create(:award_emoji, :downvote, awardable: issue)
280 281 282 283 284 285 286
    end

    it "returns correct values" do
      expect(issue.upvotes).to eq(1)
      expect(issue.downvotes).to eq(1)
    end
  end
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316

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