Search code examples
ruby-on-railstestingcucumbercapybara

Unable to find link "New country" (Capybara::ElementNotFound)


Please, help! I run the test (cucumber) and it gives this error : Unable to find link "New country" (Capybara::ElementNotFound)

    <% content_for :page_header do %>
  <% render partial: "layouts/app_page_header" , locals: { page_title: "Countries" } %>
<% end %>

<div class="flex flex-col">
  <div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
    <div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
      <div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
        <table class="min-w-full divide-y divide-gray-200">
          <thead>
            <tr>
              <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                Country code
              </th>
              <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                Country name
              </th>
              <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                Cities count
              </th>
              <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                Legal forms count
              </th>
              <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                Taxation forms count
              </th>
              <th scope="col" class="px-6 py-3 bg-gray-50">
                <span class="sr-only">Actions</span>
              </th>
            </tr>
          </thead>

          <tbody class="bg-white divide-y divide-gray-200" id="countries">
            <%= render @countries %>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</div> <br>

<%= link_to t('.new_country_button'), new_admin_country_path, id:'new_admin_country_submit', class: 'btn btn-primary' %>

<p>
  <%== render partial: "shared/pagy" , locals: {pagy: @pagy} if @pagy.pages> 1 %>
</p>

when I enter "print page.html" it shows that I am on the correct page and I don’t understand why I don’t find the "New country" button I need.

UPD so I ran the command "puts page.body" in the console and got the html


<!DOCTYPE html>
      <html>
        <head>
          <title>CosmoPort</title>
          
          
      
          
          <script src="/packs-test/js/application-0a4f8f4726a3a8685a4c.js"></script>
        </head>
      
        <body>
          <div
            x-data="{ sidebarOpen: false }"
            @keydown.window.escape="sidebarOpen = false"
            class="h-screen flex overflow-hidden bg-gray-100">
            <!-- Off-canvas menu for mobile, show/hide based on off-canvas menu state. -->
            <div
              x-show="sidebarOpen"
              x-description="Off-canvas menu for mobile, show/hide based on off-canvas menu state."
              class="md:hidden">
              <div class="fixed inset-0 flex z-40">
                <div
                  @click="sidebarOpen = false"
                  x-show="sidebarOpen"
                  x-description="Off-canvas menu overlay, show/hide based on off-canvas menu state."
                  x-transition:enter="transition-opacity ease-linear duration-300"
                  x-transition:enter-start="opacity-0"
                  x-transition:enter-end="opacity-100"
                  x-transition:leave="transition-opacity ease-linear duration-300"
                  x-transition:leave-start="opacity-100"
                  x-transition:leave-end="opacity-0"
                  class="fixed inset-0" aria-hidden="true">
                  <div class="absolute inset-0 bg-gray-600 opacity-75"></div>
                </div>
                <div
                  x-show="sidebarOpen"
                  x-description="Off-canvas menu, show/hide based on off-canvas menu state."
                  x-transition:enter="transition ease-in-out duration-300 transform"
                  x-transition:enter-start="-translate-x-full"
                  x-transition:enter-end="translate-x-0"
                  x-transition:leave="transition ease-in-out duration-300 transform"
                  x-transition:leave-start="translate-x-0"
                  x-transition:leave-end="-translate-x-full"
                  class="relative flex-1 flex flex-col max-w-xs w-full pt-5 pb-4 bg-gray-800">
                  <div class="absolute top-0 right-0 -mr-12 pt-2">
                    <button
                      x-show="sidebarOpen"
                      @click="sidebarOpen = false"
                      class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
                      <span class="sr-only">Close sidebar</span>
                      <!-- Heroicon name: x -->
                      <svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
                      </svg>
                    </button>
                  </div>
                  <div class="flex-shrink-0 flex items-center px-4">
                    <img class="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-logo-indigo-500-mark-white-text.svg" alt="Workflow">
                  </div>
                  <div class="mt-5 flex-1 h-0 overflow-y-auto">
                    <nav class="px-2 space-y-1">
                      <a class="sidebar-navigation-link" href="/">
        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
        </svg>
        Dashboard
      </a>
        <a class="sidebar-navigation-link active" aria-current="page" href="/admin">
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
               aria-hidden="true">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                  d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
          </svg>
          Admin
      </a><br/>
      
        <div class="space-y-1">
        <h3 class="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider" id="admin-headline">
          Admin
        </h3>
        <ul class="space-y-1" role="group" aria-labelledby="admin-headline">
          <li>
            <a class="sidebar-submenu-link" href="/admin/users">Users</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link active" aria-current="page" href="/admin/countries">Countries</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/cities">Cities</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/legal_forms">Legal forms</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/taxation_forms">Taxation forms</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/businesses">Businesses</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/conversations">Conversations</a>
          </li>
        </ul>
      </div>
      
      
                    </nav>
                  </div>
                </div>
                <div class="flex-shrink-0 w-14" aria-hidden="true">
                  <!-- Dummy element to force sidebar to shrink to fit close icon -->
                </div>
              </div>
            </div>
      
            <!-- Static sidebar for desktop -->
            <div class="hidden md:flex md:flex-shrink-0">
              <div class="flex flex-col w-64">
                <!-- Sidebar component, swap this element with another sidebar if you like -->
                <div class="flex flex-col h-0 flex-1">
                  <div class="flex items-center h-16 flex-shrink-0 px-4 bg-gray-900">
                    <img class="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-logo-indigo-500-mark-white-text.svg" alt="Workflow">
                  </div>
                  <div class="flex-1 flex flex-col overflow-y-auto">
                    <nav class="flex-1 px-2 py-4 bg-gray-800 space-y-1">
                      <a class="sidebar-navigation-link" href="/">
        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
        </svg>
        Dashboard
      </a>
        <a class="sidebar-navigation-link active" aria-current="page" href="/admin">
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"
               aria-hidden="true">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                  d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/>
          </svg>
          Admin
      </a><br/>
      
        <div class="space-y-1">
        <h3 class="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider" id="admin-headline">
          Admin
        </h3>
        <ul class="space-y-1" role="group" aria-labelledby="admin-headline">
          <li>
            <a class="sidebar-submenu-link" href="/admin/users">Users</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link active" aria-current="page" href="/admin/countries">Countries</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/cities">Cities</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/legal_forms">Legal forms</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/taxation_forms">Taxation forms</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/businesses">Businesses</a>
          </li>
      
          <li>
            <a class="sidebar-submenu-link" href="/admin/conversations">Conversations</a>
          </li>
        </ul>
      </div>
      
      
                    </nav>
                  </div>
                </div>
              </div>
            </div>
            <div class="flex flex-col w-0 flex-1 overflow-hidden">
              <div class="relative z-10 flex-shrink-0 flex h-16 bg-white shadow">
                <button
                  @click.stop="sidebarOpen = true"
                  class="px-4 border-r border-gray-200 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500 md:hidden">
                  <span class="sr-only">Open sidebar</span>
                  <!-- Heroicon name: menu-alt-2 -->
                  <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h7"/>
                  </svg>
                </button>
                <div class="flex-1 px-4 flex justify-end">
      
                  <div class="ml-4 flex items-center md:ml-6">
                    <button class="bg-white p-1 rounded-full text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                      <span class="sr-only">View notifications</span>
                      <!-- Heroicon name: bell -->
                      <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
                      </svg>
                    </button>
      
                    <!-- Profile dropdown -->
                    <div @click.away="open = false" x-data="{ open: false }" class="ml-3 relative">
                      <div>
                        <button
                          @click="open = !open"
                          x-bind:aria-expanded="open"
                          class="max-w-xs bg-white flex items-center text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" id="user-menu" aria-haspopup="true">
                          <span class="sr-only">Open user menu</span>
                            <svg class="h-8 w-8 text-gray-600 rounded-full" fill="currentColor" viewBox="0 0 24 24">
                              <path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z"></path>
                            </svg>
                        </button>
                      </div>
                      <!--
                        Profile dropdown panel, show/hide based on dropdown state.
      
                        Entering: "transition ease-out duration-100"
                          From: "transform opacity-0 scale-95"
                          To: "transform opacity-100 scale-100"
                        Leaving: "transition ease-in duration-75"
                          From: "transform opacity-100 scale-100"
                          To: "transform opacity-0 scale-95"
                      -->
                      <div
                        x-show="open"
                        x-description="Profile dropdown panel, show/hide based on dropdown state."
                        x-transition:enter="transition ease-out duration-100"
                        x-transition:enter-start="transform opacity-0 scale-95"
                        x-transition:enter-end="transform opacity-100 scale-100"
                        x-transition:leave="transition ease-in duration-75"
                        x-transition:leave-start="transform opacity-100 scale-100"
                        x-transition:leave-end="transform opacity-0 scale-95"
                        class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
                          <a role="menuitem" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" href="/users/edit">Account</a>
        <a role="menuitem" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" href="/profiles/edit">Edit profile</a>
        <a role="menuitem" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" rel="nofollow" data-method="delete" href="/users/sign_out">Logout</a>
      
      
                      </div>
                    </div>
                  </div>
                </div>
              </div>
      
              <main
                x-data=""
                x-init="$el.focus()"
                class="flex-1 relative overflow-y-auto focus:outline-none" tabindex="0">
                <div class="py-6">
                  <div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
                      <h1 class="text-2xl font-bold text-gray-900">
        Countries
      </h1>
      
                  </div>
                  <div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
                    
                    
      <div class="flex flex-col">
        <div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
          <div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
            <div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
              <table class="min-w-full divide-y divide-gray-200">
                <thead>
                  <tr>
                    <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                      Country code
                    </th>
                    <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                      Country name
                    </th>
                    <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                      Cities count
                    </th>
                    <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                      Legal forms count
                    </th>
                    <th scope="col" class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                      Taxation forms count
                    </th>
                    <th scope="col" class="px-6 py-3 bg-gray-50">
                      <span class="sr-only">Actions</span>
                    </th>
                  </tr>
                </thead>
      
                <tbody class="bg-white divide-y divide-gray-200" id="countries">
                  <tr>
        <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">RU</td>
        <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Russian Federation</td>
        <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">0</td>
        <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">0</td>
        <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">0</td>
        <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
          <a class="text-indigo-600 hover:text-indigo-900" href="/admin/countries/eb1c9426-8d31-443d-8131-12d5026ec220">Show</a> |
          <a class="text-indigo-600 hover:text-indigo-900" href="/admin/countries/eb1c9426-8d31-443d-8131-12d5026ec220/edit">Edit</a> |
          <a data-confirm="Are you sure?" class="text-red-600 hover:text-red-900" rel="nofollow" data-method="delete" href="/admin/countries/eb1c9426-8d31-443d-8131-12d5026ec220">Delete</a>
        </td>
      </tr>
      
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div> <br>
      
      <a id="new_admin_country_submit" class="btn btn-primary" href="/admin/countries/new">New country</a>
      
      <p>
        
      </p>
      
                  </div>
                </div>
              </main>
            </div>
          </div>
      
          <footer class="footer">
            <div class="container mx-auto mt-5">
              &copy; <span id="current_year"></span> CosmoZoo Inc, All rights reserved.
            </div>
          </footer>
        </body>
      </html>

test code ( the required line starts with #New country):

password = (0...20).map { ("a".."z").to_a[rand(26)] }.join

# See countries
Given("I am an admin") do
  admin_user = FactoryBot.create(:user, name: "Tim Cook", email: "[email protected]", password: password, confirmed_at: Time.zone.today)
  admin_user.add_role :admin

  expect(admin_user.has_role?(:admin)).to be_truthy
  expect(admin_user).not_to be_nil
end

When("I am log in as an admin") do
  visit user_session_path

  fill_in "user_email", with: "[email protected]"
  fill_in "user_password", with: password
  click_button "Log in"
end

Then("I should be logged in as an admin") do
  expect(page).to have_content("Signed in successfully.")
end

When("I create countries") do
  FactoryBot.create(:country,
    country_code: "RU")
end

Then("I should see countries") do
  visit admin_dashboard_path
  click_link("Countries", match: :first)
  expect(page).to have_content("RU")
  puts page.body
end

# New country
When "I submit a new country" do
  click_link "New country"
end

Then("I should see the new country") do
  pending # Write code here that turns the phrase above into concrete actions
end

Solution

  • From the HTML the element should match the :link selector. That means there's really only a couple of possible reasons.

    1. The link isn't actually visible on the page - add page.save_and_open_screenshot before the click_link to see what the page actually looks like. Is it possible you're not running the browser wide enough, and it's collapsing to a mobile layout or something?

    2. You have CSS being applied to the link which is changing the case of the text - screenshot would show that too

    One other thing to try would be click_link('new_admin_country_submit') and see if that actually finds and clicks the link (which would indicate an issue with text case, etc)