When you repeat complex assertions in your tests multiple times, it might be a good idea to extract a custom RSpec matcher. This not only tidies up your own code, but also makes it easier to write future specs.
However, not all situations are well-suited for that. Sometimes the effort isn't worth it, e.g. when your matcher would need too many parameters to be reusable. In those cases, you can also simply extract a private method:
describe `/search` do
  it "doesn't lose the infinite scroll's filter state when navigating backwards" do
    create(:event, :published, name: '1st match', start_date: 1.week.from_now)
    create(:event, :published, name: '2nd match', start_date: 1.week.from_now)
    create(:event, :published, name: 'Discarded', start_date: 2.years.from_now)
    visit(search_path(start_date: 'next_3_months'))
    expect_item_card_to_contain('1st match')
    expect_infinite_scroll_to_be_filtered
    within('.layout--infinite-scroll') do
      click_link('2nd match')
    end
    expect_item_card_to_contain('2nd match')
    expect_infinite_scroll_to_be_filtered
    within('.layout--infinite-scroll') do
      click_link('1st match')
    end
    expect_item_card_to_contain('1st match')
    expect_infinite_scroll_to_be_filtered
    page.go_back
    expect_item_card_to_contain('2nd match')
    expect_infinite_scroll_to_be_filtered
    page.go_back
    expect_item_card_to_contain('1st match')
    expect_infinite_scroll_to_be_filtered
  end
  private
  def expect_infinite_scroll_to_be_filtered
    within('.layout--infinite-scroll') do
      expect(page).to have_text('1st match')
      expect(page).to have_text('2nd match')
      expect(page).not_to have_text('Discarded')
    end
  end
  def expect_item_card_to_contain(text)
    within('.layout--item-card') do
      expect(page).to have_text(text)
    end
  end
end
See also
Posted by Klaus Weidinger to makandra dev (2025-07-10 08:58)