Search code examples
rubycapybararuby-on-rails-5capybara-webkit

Capybara Webkit, find specific li element, click delete button and check if the specific li element is missing


I want to test simple delete jquery/ajax , using capybara-webkit.

User clicks delete button, within <li>, after that element should be gone.

My html looks like this:

<div class="artist-songs">
    <h3 tabindex="-1" id="song_h" class="song_h">Songs:</h3>
    <ol id="song-list">
        <li class="song">
            <span>The Plug</span>
            <span>
                -
                2000
                -
            </span>
            <span>Downtempo</span>
            <button name="button" type="submit" data-artist-id="5" data-song-id="14" class="delete del_song">Delete</button>
        </li>

I decided to generate Models withing test like this:

feature 'Testing song methods', js: true do
  let(:test_artist) { create :artist, name: 'Band' }

  let!(:song1) { create :song, title: 'Song 1', year: 1987, genre: 'Hip Hop', artist: test_artist }
  let!(:song2) { create :song, title: 'Song 2', artist: test_artist }
  let!(:song3) { create :song, title: 'Song 3', artist: test_artist }
  let!(:song4) { create :song, title: 'Song 4', artist: test_artist }


  scenario 'Delets first song' do
    visit artist_path(test_artist.id)
  end
end

I get the basic idea how test simple actions, however I am not sure how write a test so I am sure the correct <li> element is deleted not a random one.

So for example "Song 1" only is deleted.


Solution

  • To test deleting song1 you could do something like

    scenario 'Delete song 1' do
      visit artist_path(test_artist.id)
      within('#song-list') do
        find(:css, 'li.song', text: song1.title).click_button('Delete')
        expect(page).not_to have_css('li.song', text: song1.title) # RSpec
        # page.assert_no_selector(:css, 'li.song', text: song1.title) # if not using RSpec
    end
    

    Note: the :css parameter can be omitted if Capybara.default_selector == :css (which is the default)

    If you're going to be dealing with a lot of li.song elements in your tests you could also define a custom selector like

    Capybara.add_selector(:song) do
      xpath { |title| XPath.css('li.song')[XPath.string.n.is(title)] }
    end
    

    which means you could then do

    find(:song, song1.title).click_button('Delete')
    expect(page.not_to have_selector(:song, song1.title)