Chapter 17

3-7. ナビゲーションバー作成

Masuyama
Masuyama
2022.10.15に更新

ナビゲーションバー作成

今回は、ユーザー登録ページ (/users/sign_up) やログインページ (/users/sign_in) へのリンクを置くナビゲーションバーを作成しましょう。

仕様の確認

先に完成形を確認しておきます。
ログインしている時と、ログインしていない時では必要なリンクが異なるので使い分けていきます。

ログインしていない場合

ユーザー登録、ログイン画面へのリンクが表示されます。

image

ログインしている場合

ログアウトリンクが表示されます。

image

RSpec でのログイン設定

上記の仕様の通り、今回は「ログインしているかどうか」によって表示されるリンクが変わります。
しかし、テスト内でログインが必要が操作をする前に毎回ログインフォームからログインをするのは面倒です。

これを解決するために、devise のヘルパーメソッドを使えるようにします。
この設定をしておくと sign_in user という形で 1 行記述するだけで、RSpec テスト内でログインした状態を実現できます。

そのため、spec/rails_helper.rbの最後の方で Devise::Test::IntegrationHelpersというモジュールを include しておきます。

spec/rails_helper.rb

  ...
  config.include FactoryBot::Syntax::Methods

  config.include Devise::Test::IntegrationHelpers, type: :system # 追加
end

これで System Spec の中で sign_in userという 1 行で、user でログインした状態を実現することが出来ます。
※事前に user には User オブジェクトを代入しておく必要はあります。

このように devise gem には便利なヘルパーメソッドがいくつか用意されているので、知れば知るほど活用の幅が広がります。

テスト追加

どの画面でも共通的に表示するものですので、どの画面の System Spec でチェックしても問題ありませんが、
例えばログイン画面はログインしているとトップページへリダイレクトされてしまうため、チェックには不向きです。

今回は既に作ってあるトップページ (Home#top) の System Spec を修正しましょう。

spec/system/home_spec.rb

require 'rails_helper'

RSpec.describe 'Home', type: :system do
  before do
    driven_by :selenium_chrome_headless
  end

  describe 'トップページアクセスの検証' do
    it 'Home#top という文字列が表示される' do
      visit '/'

      expect(page).to have_content('Home#top')
    end
  end

  ####### ここから追加 #######
  describe 'ナビゲーションバーの検証' do
    context 'ログインしていない場合' do
      before { visit '/' }

      it 'ユーザー登録リンクを表示する' do
        expect(page).to have_link('ユーザー登録', href: '/users/sign_up')
      end

      it 'ログインリンクを表示する' do
        expect(page).to have_link('ログイン', href: '/users/sign_in')
      end

      it 'ログアウトリンクは表示しない' do
        expect(page).not_to have_content('ログアウト')
      end
    end

    context 'ログインしている場合' do
      before do
        user = create(:user) # ログイン用のユーザーを作成
        sign_in user # 作成したユーザーでログイン
        visit '/'
      end

      it 'ユーザー登録リンクは表示しない' do
        expect(page).not_to have_link('ユーザー登録', href: '/users/sign_up')
      end

      it 'ログインリンクは表示しない' do
        expect(page).not_to have_link('ログイン', href: '/users/sign_in')
      end

      it 'ログアウトリンクを表示する' do
        expect(page).to have_content('ログアウト')
      end

      it 'ログアウトリンクが機能する' do
        click_button 'ログアウト'

        # ログインしていない状態のリンク表示パターンになることを確認
        expect(page).to have_link('ユーザー登録', href: '/users/sign_up')
        expect(page).to have_link('ログイン', href: '/users/sign_in')
        expect(page).not_to have_content('ログアウト')
      end
    end
  end
end

この時点では当然、テストがコケることを確認します。
ただし、一部リンクが「表示されないこと」を確認しているテストもあるため、一部のテストはコケないことに注意してください。

$ bin/rspec spec/system/home_spec.rb
...
Finished in 11.44 seconds (files took 0.45402 seconds to load)
8 examples, 4 failures

Failed examples:

rspec ./spec/system/home_spec.rb:20 # Home ナビゲーションバーの検証 ログインしていない場合 ユーザー登録リンクを表示する
rspec ./spec/system/home_spec.rb:24 # Home ナビゲーションバーの検証 ログインしていない場合 ログインリンクを表示する
rspec ./spec/system/home_spec.rb:48 # Home ナビゲーションバーの検証 ログインしている場合 ログアウトリンクを表示する
rspec ./spec/system/home_spec.rb:52 # Home ナビゲーションバーの検証 ログインしている場合 ログアウトリンクが機能する

実装

では、上記のテストが通るように設定していきましょう。

部分テンプレート作成

app/views/shared ディレクトリに _navbar.html.erb というファイルを作成します。

<nav class="bg-gray-800 border-gray-200 px-2 sm:px-4 py-2.5">
  <div class="container flex flex-wrap justify-between items-center mx-auto">
    <%= link_to "TechLog", "/", class: "self-center text-white text-xl font-semibold whitespace-nowrap" %>
    <div class="w-full md:block md:w-auto">
      <ul class="flex flex-col mt-4 md:flex-row md:space-x-8 md:mt-0 md:text-sm md:font-medium">
        <% if current_user %>
          <li>
            <%= button_to "ログアウト", destroy_user_session_path, class: "block py-2 pr-4 pl-3 text-gray-400 hover:text-white border-b border-gray-700 hover:bg-gray-700 md:hover:bg-transparent md:border-0 md:hover:text-blue-white md:p-0", method: :delete %>
          </li>
        <% else %>
          <li>
            <%= link_to "ユーザー登録", new_user_registration_path, class: "block py-2 pr-4 pl-3 text-gray-400 hover:text-white border-b border-gray-700 hover:bg-gray-700 md:hover:bg-transparent md:border-0 md:hover:text-blue-white md:p-0" %>
          </li>
          <li>
            <%= link_to "ログイン", new_user_session_path, class: "block py-2 pr-4 pl-3 text-gray-400 hover:text-white border-b border-gray-700 hover:bg-gray-700 md:hover:bg-transparent md:border-0 md:hover:text-blue-white md:p-0" %>
          </li>
        <% end %>
      </ul>
    </div>
  </div>
</nav>

ちなみに、ログアウトリンクだけは link_to ではなく button_to となっていることに注意してください。
これは、デフォルトでは link_tomethod: :delete をただ追加しても DELET メソッドを実現できないためです。

さらに、フラッシュメッセージの部分テンプレートを読み込んだ時と同じく
今回作成した部分テンプレートを app/views/layouts/application.html.erb に追記します。

app/views/layouts/application.html.erb

...
  <body class="h-screen bg-blue-50">
    <%= render 'shared/flash' %>
    <%= render 'shared/navbar' %> <%# 追記 %>
    <main class="container mx-auto mt-20 py-8 px-5 flex items-center justify-center">
      <%= yield %>
    </main>
  </body>
</html>

テスト実行

実装が完了しましたので、テストがすべて成功することを確認します。

$ bin/rspec spec/system/home_spec.rb
...
  ナビゲーションバーの検証
    ログインしていない場合
      ユーザー登録リンクを表示する
      ログインリンクを表示する
      ログアウトリンクは表示しない
    ログインしている場合
      ユーザー登録リンクは表示しない
      ログインリンクは表示しない
      ログアウトリンクを表示する
      ログアウトリンクが機能する

Finished in 3.45 seconds (files took 0.41146 seconds to load)
8 examples, 0 failures

動作確認

すべてのテストに通りましたが、見た目としても問題ないことを確認します。

bin/devコマンドで開発用サーバを起動してから、各リンクを確認していきます。

ログインしていない場合

最初はログアウトした状態で、ユーザー登録リンクとログインリンクが表示されることを確認します。

<img width="235" alt="image" src="https://user-images.githubusercontent.com/68495563/180642033-97ef6b2a-d904-4d9a-ab8f-e44bdeeb2dac.png">

また、それぞれのリンクをクリックし、各ページへ遷移できることも確認しておくといいでしょう。

ログインしている場合

今度はログインした時のナビゲーションバーを確認します。
ログアウトリンクが表示されていれば OK です。

<img width="145" alt="image" src="https://user-images.githubusercontent.com/68495563/180642109-f0f5ecb2-6e74-4330-abc7-58bb218cc6ff.png">

ログアウトリンクをクリックするとログアウトされ、ユーザー登録とログインリンクも表示されるようになることを確認しておきます。

<img width="235" alt="image" src="https://user-images.githubusercontent.com/68495563/180642033-97ef6b2a-d904-4d9a-ab8f-e44bdeeb2dac.png">

変更をコミット

これでナビゲーションバーを実装は完了です。
変更をコミットしておきます。

$ git add .
$ git commit -m "ナビゲーションバーを追加"
$ git push

宿題

今回、Capybara でユーザー登録・ログインのテキストリンクを探すために have_link というマッチャを使いました。

have_link マッチャがどういう要素を見つけてくるのかは、前にも紹介した人気の Qiita 記事で復習しておきましょう。

deviseのRSpec用ヘルパー

RSpec の中でログイン状態を実現するためのヘルパーを使いましたね。
もう少し簡単なアプリケーションで導入する時の流れを全体像で掴んでおいた方が理解が進むので、こちらの記事で流れを改めて把握しておきましょう。