gfm_autocomplete_spec.rb 9.82 KB
Newer Older
1 2
require 'rails_helper'

3
describe 'GFM autocomplete', :js do
4
  let(:user)    { create(:user, name: '💃speciąl someone💃', username: 'someone.special') }
5
  let(:project) { create(:project) }
6
  let(:label) { create(:label, project: project, title: 'special+') }
7 8 9
  let(:issue)   { create(:issue, project: project) }

  before do
10
    project.add_maintainer(user)
11
    sign_in(user)
12
    visit project_issue_path(project, issue)
13

14
    wait_for_requests
15
  end
16

17
  it 'updates issue descripton with GFM reference' do
18
    find('.js-issuable-edit').click
19

Mike Greiling's avatar
Mike Greiling committed
20
    simulate_input('#issue-description', "@#{user.name[0...3]}")
21

22
    find('.atwho-view .cur').click
23 24 25 26 27 28

    click_button 'Save changes'

    expect(find('.description')).to have_content(user.to_reference)
  end

29
  it 'opens autocomplete menu when field starts with text' do
30
    page.within '.timeline-content-form' do
31
      find('#note-body').native.send_keys('@')
32 33
    end

Phil Hughes's avatar
Phil Hughes committed
34
    expect(page).to have_selector('.atwho-container')
35
  end
36

37
  it 'doesnt open autocomplete menu character is prefixed with text' do
38
    page.within '.timeline-content-form' do
39 40
      find('#note-body').native.send_keys('testing')
      find('#note-body').native.send_keys('@')
41 42
    end

43
    expect(page).not_to have_selector('.atwho-view')
44 45
  end

46 47
  it 'doesnt select the first item for non-assignee dropdowns' do
    page.within '.timeline-content-form' do
48
      find('#note-body').native.send_keys(':')
49 50 51 52
    end

    expect(page).to have_selector('.atwho-container')

53
    wait_for_requests
54 55 56 57

    expect(find('#at-view-58')).not_to have_selector('.cur:first-of-type')
  end

58
  it 'does not open autocomplete menu when ":" is prefixed by a number and letters' do
59
    note = find('#note-body')
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

    # Number.
    page.within '.timeline-content-form' do
      note.native.send_keys('7:')
    end

    expect(page).not_to have_selector('.atwho-view')

    # ASCII letter.
    page.within '.timeline-content-form' do
      note.set('')
      note.native.send_keys('w:')
    end

    expect(page).not_to have_selector('.atwho-view')

    # Non-ASCII letter.
    page.within '.timeline-content-form' do
      note.set('')
      note.native.send_keys('Ё:')
    end

    expect(page).not_to have_selector('.atwho-view')
  end

85 86
  it 'selects the first item for assignee dropdowns' do
    page.within '.timeline-content-form' do
87
      find('#note-body').native.send_keys('@')
88 89 90 91
    end

    expect(page).to have_selector('.atwho-container')

92
    wait_for_requests
93 94 95 96

    expect(find('#at-view-64')).to have_selector('.cur:first-of-type')
  end

97 98
  it 'includes items for assignee dropdowns with non-ASCII characters in name' do
    page.within '.timeline-content-form' do
99
      find('#note-body').native.send_keys('')
Mike Greiling's avatar
Mike Greiling committed
100
      simulate_input('#note-body', "@#{user.name[0...8]}")
101 102 103 104
    end

    expect(page).to have_selector('.atwho-container')

105
    wait_for_requests
106 107 108 109

    expect(find('#at-view-64')).to have_content(user.name)
  end

110 111
  it 'selects the first item for non-assignee dropdowns if a query is entered' do
    page.within '.timeline-content-form' do
112
      find('#note-body').native.send_keys(':1')
113 114 115 116
    end

    expect(page).to have_selector('.atwho-container')

117
    wait_for_requests
118 119 120 121

    expect(find('#at-view-58')).to have_selector('.cur:first-of-type')
  end

122 123
  context 'if a selected value has special characters' do
    it 'wraps the result in double quotes' do
124
      note = find('#note-body')
125
      page.within '.timeline-content-form' do
126
        find('#note-body').native.send_keys('')
Mike Greiling's avatar
Mike Greiling committed
127
        simulate_input('#note-body', "~#{label.title[0]}")
128 129 130 131 132
      end

      label_item = find('.atwho-view li', text: label.title)

      expect_to_wrap(true, label_item, note, label.title)
133 134
    end

135
    it "shows dropdown after a new line" do
136
      note = find('#note-body')
137 138 139 140 141 142 143 144 145 146
      page.within '.timeline-content-form' do
        note.native.send_keys('test')
        note.native.send_keys(:enter)
        note.native.send_keys(:enter)
        note.native.send_keys('@')
      end

      expect(page).to have_selector('.atwho-container')
    end

147
    it "does not show dropdown when preceded with a special character" do
148
      note = find('#note-body')
149 150 151 152 153 154 155 156 157 158 159 160 161
      page.within '.timeline-content-form' do
        note.native.send_keys("@")
      end

      expect(page).to have_selector('.atwho-container')

      page.within '.timeline-content-form' do
        note.native.send_keys("@")
      end

      expect(page).to have_selector('.atwho-container', visible: false)
    end

162
    it "does not throw an error if no labels exist" do
163
      note = find('#note-body')
164 165 166 167 168 169 170
      page.within '.timeline-content-form' do
        note.native.send_keys('~')
      end

      expect(page).to have_selector('.atwho-container', visible: false)
    end

171
    it 'doesn\'t wrap for assignee values' do
172
      note = find('#note-body')
173 174 175 176 177 178 179 180 181 182
      page.within '.timeline-content-form' do
        note.native.send_keys("@#{user.username[0]}")
      end

      user_item = find('.atwho-view li', text: user.username)

      expect_to_wrap(false, user_item, note, user.username)
    end

    it 'doesn\'t wrap for emoji values' do
183
      note = find('#note-body')
184
      page.within '.timeline-content-form' do
185
        note.native.send_keys(":cartwheel_")
186 187 188 189 190 191 192
      end

      emoji_item = find('.atwho-view li', text: 'cartwheel_tone1')

      expect_to_wrap(false, emoji_item, note, 'cartwheel_tone1')
    end

193 194
    it 'doesn\'t open autocomplete after non-word character' do
      page.within '.timeline-content-form' do
195
        find('#note-body').native.send_keys("@#{user.username[0..2]}!")
196 197 198 199 200 201 202
      end

      expect(page).not_to have_selector('.atwho-view')
    end

    it 'doesn\'t open autocomplete if there is no space before' do
      page.within '.timeline-content-form' do
203
        find('#note-body').native.send_keys("hello:#{user.username[0..2]}")
204 205 206 207 208
      end

      expect(page).not_to have_selector('.atwho-view')
    end

209
    it 'triggers autocomplete after selecting a quick action' do
210
      note = find('#note-body')
211 212 213 214
      page.within '.timeline-content-form' do
        note.native.send_keys('/as')
      end

215 216
      find('.atwho-view li', text: '/assign')
      note.native.send_keys(:tab)
217 218 219 220

      user_item = find('.atwho-view li', text: user.username)
      expect(user_item).to have_content(user.username)
    end
221
  end
222

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
  # This context has jsut one example in each contexts in order to improve spec performance.
  context 'labels' do
    let!(:backend)          { create(:label, project: project, title: 'backend') }
    let!(:bug)              { create(:label, project: project, title: 'bug') }
    let!(:feature_proposal) { create(:label, project: project, title: 'feature proposal') }

    context 'when no labels are assigned' do
      it 'shows labels' do
        note = find('#note-body')

        # It should show all the labels on "~".
        type(note, '~')
        expect_labels(shown: [backend, bug, feature_proposal])

        # It should show all the labels on "/label ~".
        type(note, '/label ~')
        expect_labels(shown: [backend, bug, feature_proposal])

        # It should show all the labels on "/relabel ~".
        type(note, '/relabel ~')
        expect_labels(shown: [backend, bug, feature_proposal])

        # It should show no labels on "/unlabel ~".
        type(note, '/unlabel ~')
        expect_labels(not_shown: [backend, bug, feature_proposal])
      end
    end

    context 'when some labels are assigned' do
      before do
        issue.labels << [backend]
      end

      it 'shows labels' do
        note = find('#note-body')

        # It should show all the labels on "~".
        type(note, '~')
        expect_labels(shown: [backend, bug, feature_proposal])

        # It should show only unset labels on "/label ~".
        type(note, '/label ~')
        expect_labels(shown: [bug, feature_proposal], not_shown: [backend])

        # It should show all the labels on "/relabel ~".
        type(note, '/relabel ~')
        expect_labels(shown: [backend, bug, feature_proposal])

        # It should show only set labels on "/unlabel ~".
        type(note, '/unlabel ~')
        expect_labels(shown: [backend], not_shown: [bug, feature_proposal])
      end
    end

    context 'when all labels are assigned' do
      before do
        issue.labels << [backend, bug, feature_proposal]
      end

      it 'shows labels' do
        note = find('#note-body')

        # It should show all the labels on "~".
        type(note, '~')
        expect_labels(shown: [backend, bug, feature_proposal])

        # It should show no labels on "/label ~".
        type(note, '/label ~')
        expect_labels(not_shown: [backend, bug, feature_proposal])

        # It should show all the labels on "/relabel ~".
        type(note, '/relabel ~')
        expect_labels(shown: [backend, bug, feature_proposal])

        # It should show all the labels on "/unlabel ~".
        type(note, '/unlabel ~')
        expect_labels(shown: [backend, bug, feature_proposal])
      end
    end
  end

  private

306 307 308
  def expect_to_wrap(should_wrap, item, note, value)
    expect(item).to have_content(value)
    expect(item).not_to have_content("\"#{value}\"")
309

310
    item.click
311

312 313 314 315
    if should_wrap
      expect(note.value).to include("\"#{value}\"")
    else
      expect(note.value).not_to include("\"#{value}\"")
316
    end
317
  end
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340

  def expect_labels(shown: nil, not_shown: nil)
    page.within('.atwho-container') do
      if shown
        expect(page).to have_selector('.atwho-view li', count: shown.size)
        shown.each { |label| expect(page).to have_content(label.title) }
      end

      if not_shown
        expect(page).not_to have_selector('.atwho-view li') unless shown
        not_shown.each { |label| expect(page).not_to have_content(label.title) }
      end
    end
  end

  # `note` is a textarea where the given text should be typed.
  # We don't want to find it each time this function gets called.
  def type(note, text)
    page.within('.timeline-content-form') do
      note.set('')
      note.native.send_keys(text)
    end
  end
341
end