diff --git a/.gitignore b/.gitignore index 9cbc5dbfc..91e932c2b 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ yarn-debug.log* .yarn-integrity /config/credentials/production.key + +/vendor/bundle +.DS_Store diff --git a/Gemfile b/Gemfile index 059b8fabb..0f83f78f4 100644 --- a/Gemfile +++ b/Gemfile @@ -46,6 +46,9 @@ group :development, :test do gem 'byebug', platforms: %i[mri mingw x64_mingw] gem 'rb-readline' gem 'rspec-rails' + gem 'capybara', '~> 3.23' + gem 'selenium-webdriver', '4.9.0' + gem 'webdrivers' end group :development do @@ -69,3 +72,5 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] + +gem 'nokogiri', '1.12.5' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 4d19f0151..b3e28dbf4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,6 +106,15 @@ GEM msgpack (~> 1.0) builder (3.2.4) byebug (11.1.3) + capybara (3.39.2) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) concurrent-ruby (1.1.9) crass (1.0.6) diff-lcs (1.4.4) @@ -173,6 +182,7 @@ GEM mail (2.7.1) mini_mime (>= 0.1.1) marcel (1.0.2) + matrix (0.4.2) method_source (1.0.0) mini_mime (1.1.2) minitest (5.14.4) @@ -284,6 +294,7 @@ GEM rubocop (>= 1.7.0, < 2.0) ruby-progressbar (1.11.0) ruby2_keywords (0.0.5) + rubyzip (2.3.2) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) @@ -294,6 +305,10 @@ GEM sprockets (> 3.0) sprockets-rails tilt + selenium-webdriver (4.9.0) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) semantic_range (3.0.0) sorcery (0.16.1) bcrypt (~> 3.1) @@ -318,24 +333,33 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) + webdrivers (5.2.0) + nokogiri (~> 1.6) + rubyzip (>= 1.3.0) + selenium-webdriver (~> 4.0) webpacker (5.4.3) activesupport (>= 5.2) rack-proxy (>= 0.6.1) railties (>= 5.2) semantic_range (>= 2.3.0) + websocket (1.2.9) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) zeitwerk (2.5.1) PLATFORMS x86_64-darwin-19 + x86_64-darwin-22 x86_64-linux DEPENDENCIES aws-sdk-s3 bootsnap (>= 1.4.4) byebug + capybara (~> 3.23) draper enum_help factory_bot_rails @@ -345,6 +369,7 @@ DEPENDENCIES kaminari! letter_opener_web listen (~> 3.3) + nokogiri (= 1.12.5) pg puma (~> 5.0) rack-mini-profiler (~> 2.0) @@ -356,11 +381,13 @@ DEPENDENCIES rubocop rubocop-rails sass-rails (>= 6) + selenium-webdriver (= 4.9.0) sorcery spring sqlite3 (~> 1.4) tzinfo-data web-console (>= 4.1.0) + webdrivers webpacker (~> 5.0) RUBY VERSION diff --git a/README.md b/README.md index 55e470590..670a5f51b 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,17 @@ $ bin/rails s ``` ## 事業をエンジニアリングしよう提案編の回答は以下に記述してください + +【選択した事業側の課題】 +直近一年間で、2回以上もくもく会に参加してくれた人は利用者全体の1%のみ。もくもく会で気の合う仲間を見つけられなかったのではないか? + +【提案内容】 +参加者のレベル別(具体的には、エンジニア経験年数別)でもくもく会を開催できるようにする。 +→ +① エンジニア経験の長いユーザーは、似たレベルのユーザーが多い場で知識共有ができるようになる。それによってもくもく会の価値が高まり再参加を見込めるようになる。エンジニア経験の浅いユーザーは、参加する際の心理的ハードルが下がることで、継続的な参加がしやすくなる。 +② 運営側は、年数別の選択肢を設けることにより、必然的に経験年数の浅い人が初めて参加することを見込めるようにもなる。 + +【実装方針】 +・もくもく会作成時に参加対象者の指定を必須にする。あらかじめ「3年未満」、「4〜9年」、「10年以上」、「全員」といった選択肢(タグ)を作成しておき、セレクトボックスによってその選択ができるようにする。イベント作成者は、自分が該当する年数か全員かのどちらかを選択した上でイベントを作成する。 +・ヘッダーにある検索フォームで、上記で作成した選択肢(タグ)による絞り込み検索ができるようにする。その際、あらかじめすべての選択肢を並べて表示させておき、ユーザーがそのうちの1つを押下すれば該当するイベントの絞り込みができるようにする。このようにタグをあらかじめ表示させておくことによって、そもそもそのような分類でイベントが開催されているという情報を、ユーザーに即座に与えることができる。 +・イベント一覧画面に表示されるイベント概要(現在はタイトル, 内容, 開催地, 開催日時, 作成者が表示されている部分)に、参加対象のタグも表示させる \ No newline at end of file diff --git a/app/controllers/followings_controller.rb b/app/controllers/followings_controller.rb new file mode 100644 index 000000000..1d9f9fe77 --- /dev/null +++ b/app/controllers/followings_controller.rb @@ -0,0 +1,12 @@ +class FollowingsController < ApplicationController + + def create + current_user.follow(params[:user_id]) + redirect_back fallback_location: users_path + end + + def destroy + current_user.unfollow(params[:user_id]) + redirect_back fallback_location: users_path + end +end diff --git a/app/controllers/mypage/profiles_controller.rb b/app/controllers/mypage/profiles_controller.rb index 966f90960..080c377ef 100644 --- a/app/controllers/mypage/profiles_controller.rb +++ b/app/controllers/mypage/profiles_controller.rb @@ -17,6 +17,6 @@ def update private def profile_params - params.require(:user).permit(:name, :avatar) + params.require(:user).permit(:name, :avatar,:email, :profile, :hobby) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 136d60eba..6bf4d9215 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -16,6 +16,10 @@ def create end end + def show + @user = User.find(params[:id]) + end + def user_params params.require(:user).permit(:email, :name, :password, :password_confirmation) end diff --git a/app/decorators/following_decorator.rb b/app/decorators/following_decorator.rb new file mode 100644 index 000000000..5eb60264f --- /dev/null +++ b/app/decorators/following_decorator.rb @@ -0,0 +1,13 @@ +class FollowingDecorator < Draper::Decorator + delegate_all + + # Define presentation-specific methods here. Helpers are accessed through + # `helpers` (aka `h`). You can override attributes, for example: + # + # def created_at + # helpers.content_tag :span, class: 'time' do + # object.created_at.strftime("%a %m/%d/%y") + # end + # end + +end diff --git a/app/models/following.rb b/app/models/following.rb new file mode 100644 index 000000000..1ec08e5bc --- /dev/null +++ b/app/models/following.rb @@ -0,0 +1,7 @@ +class Following < ApplicationRecord + belongs_to :follower, class_name: "User" + belongs_to :followed, class_name: "User" + validates :follower_id, presence: true + validates :followed_id, presence: true + validates :follower_id, uniqueness: {scope: :followed_id} +end diff --git a/app/models/user.rb b/app/models/user.rb index 894c04a65..fe0286fb6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -12,6 +12,10 @@ class User < ApplicationRecord has_many :notifications, foreign_key: :receiver_id, dependent: :destroy, inverse_of: :sender has_many :user_notification_timings, dependent: :destroy has_many :notification_timings, through: :user_notification_timings + has_many :followers, class_name: "Following", foreign_key: "follower_id", dependent: :destroy + has_many :followeds, class_name: "Following", foreign_key: "followed_id", dependent: :destroy + has_many :following_users, through: :followers, source: :followed + has_many :follower_users, through: :followeds, source: :follower has_one_attached :avatar validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } @@ -76,4 +80,16 @@ def allow_attended_to_event_notification? def allow_liked_event_notification? notification_timings.liked_event.present? end + + def follow(user_id) + followers.create(followed_id: user_id) + end + + def unfollow(user_id) + followers.find_by(followed_id: user_id).destroy + end + + def following?(user) + following_users.include?(user) + end end diff --git a/app/views/events/_attendee.html.erb b/app/views/events/_attendee.html.erb index 03c1fb938..56a213acf 100644 --- a/app/views/events/_attendee.html.erb +++ b/app/views/events/_attendee.html.erb @@ -1,6 +1,6 @@
name: <%= @user.name %>
+hobby: <%= @user.hobby %>
+profile: <%= @user.profile %>
+<% if logged_in? && current_user != @user %> + <% if current_user.following?(@user) %> + <%= link_to "フォローを外す", user_followings_path(@user.id), method: :delete %> + <% else %> + <%= link_to "フォローする", user_followings_path(@user.id), method: :post %> + <% end %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 018ec9d39..a8df93443 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,7 +9,14 @@ delete 'logout', to: 'sessions#destroy' get 'signup', to: 'users#new' post 'signup', to: 'users#create' - resources :users, only: %i[new create] + + resources :users, only: %i[new create show] do + member do + get :follows, :followers + end + resource :followings, only: %i[create destroy] + end + resources :events do collection do get :future diff --git a/db/migrate/20230622044719_add_hobby_to_users.rb b/db/migrate/20230622044719_add_hobby_to_users.rb new file mode 100644 index 000000000..1b093f031 --- /dev/null +++ b/db/migrate/20230622044719_add_hobby_to_users.rb @@ -0,0 +1,5 @@ +class AddHobbyToUsers < ActiveRecord::Migration[6.1] + def change + add_column :users, :hobby, :string + end +end diff --git a/db/migrate/20230622044750_add_profile_to_users.rb b/db/migrate/20230622044750_add_profile_to_users.rb new file mode 100644 index 000000000..4db0ecc4c --- /dev/null +++ b/db/migrate/20230622044750_add_profile_to_users.rb @@ -0,0 +1,5 @@ +class AddProfileToUsers < ActiveRecord::Migration[6.1] + def change + add_column :users, :profile, :string + end +end diff --git a/db/migrate/20230622160718_create_followings.rb b/db/migrate/20230622160718_create_followings.rb new file mode 100644 index 000000000..6889c0be1 --- /dev/null +++ b/db/migrate/20230622160718_create_followings.rb @@ -0,0 +1,13 @@ +class CreateFollowings < ActiveRecord::Migration[6.1] + def change + create_table :followings do |t| + t.integer :follower_id + t.integer :followed_id + + t.timestamps + end + add_index :followings, :follower_id + add_index :followings, :followed_id + add_index :followings, [:follower_id, :followed_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 01c4634ac..a56d1c79a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_01_19_072358) do +ActiveRecord::Schema.define(version: 2023_06_22_160718) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false @@ -82,6 +82,16 @@ t.index ["user_id"], name: "index_events_on_user_id" end + create_table "followings", force: :cascade do |t| + t.integer "follower_id" + t.integer "followed_id" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["followed_id"], name: "index_followings_on_followed_id" + t.index ["follower_id", "followed_id"], name: "index_followings_on_follower_id_and_followed_id", unique: true + t.index ["follower_id"], name: "index_followings_on_follower_id" + end + create_table "notification_timings", force: :cascade do |t| t.integer "timing", null: false t.datetime "created_at", precision: 6, null: false @@ -135,6 +145,8 @@ t.string "name", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.string "hobby" + t.string "profile" t.index ["email"], name: "index_users_on_email", unique: true end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index af314b516..aa40f6e0f 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -7,6 +7,7 @@ # Prevent database truncation if the environment is production abort('The Rails environment is running in production mode!') if Rails.env.production? require 'rspec/rails' + # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in @@ -23,6 +24,7 @@ # require only the support files necessary. # # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } +Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. @@ -65,4 +67,5 @@ # config.filter_gems_from_backtrace("gem name") config.include FactoryBot::Syntax::Methods config.include Sorcery::TestHelpers::Rails::Request, type: :request + config.include LoginMacros end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e3ed538d4..45ca46707 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -53,7 +53,7 @@ # # is tagged with `:focus`, all examples get run. RSpec also provides # # aliases for `it`, `describe`, and `context` that include `:focus` # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - # config.filter_run_when_matching :focus + config.filter_run_when_matching :focus # # # Allows RSpec to persist some state between runs in order to support # # the `--only-failures` and `--next-failure` CLI options. We recommend diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb new file mode 100644 index 000000000..bf5ac8bd3 --- /dev/null +++ b/spec/support/capybara.rb @@ -0,0 +1,5 @@ +RSpec.configure do |config| + config.before(:each, type: :system) do + driven_by :selenium, using: :headless_chrome, screen_size: [1920, 1080] + end +end diff --git a/spec/support/login_macros.rb b/spec/support/login_macros.rb new file mode 100644 index 000000000..f51c256fa --- /dev/null +++ b/spec/support/login_macros.rb @@ -0,0 +1,8 @@ +module LoginMacros + def login(user) + visit login_path + fill_in 'email', with: user.email + fill_in 'password', with: 'password' + click_button 'ログイン' + end +end diff --git a/spec/system/events_spec.rb b/spec/system/events_spec.rb new file mode 100644 index 000000000..9d2111a3b --- /dev/null +++ b/spec/system/events_spec.rb @@ -0,0 +1,191 @@ +require 'rails_helper' + +RSpec.describe "Events", type: :system do + let(:user) { create :user } + let(:event) { create :event} + + describe 'event関係' do + context 'index' do + before do + login(user) + end + + it '一覧ページにアクセスすると、イベント一覧を閲覧できる' do + event + visit root_path + expect(page).to have_content(event.title) + expect(page).to have_content(event.prefecture.name) + expect(page).to have_content(event.user.name) + end + end + + context 'new→create' do + before do + login(user) + end + + it '新しくイベント(未来)を作成できる。トップページでそれを確認できる。"直近イベント"でそれを確認できる。' do + visit new_event_path + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '002055-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(user.name) + expect(page).to have_content('開催前') + + visit root_path + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(user.name) + + visit future_events_path + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(user.name) + end + + it '新しくイベント(過去)を作成できる。ただしトップページでそれは確認できない。"過去イベント"において確認できる。' do + visit new_event_path + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '002014-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(user.name) + expect(page).to have_content('開催済み') + + visit root_path + expect(page).not_to have_content('RUNTEQもくもく会') + expect(page).not_to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).not_to have_content(user.name) + + visit past_events_path + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(user.name) + end + + it 'titleが未記入だと新しいイベントは作成されない。' do + visit new_event_path + fill_in 'Title', with: '' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '002055-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('もくもく会作成') + end + + it 'contentが未記入だと新しいイベントは作成されない。' do + visit new_event_path + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '' + fill_in 'Held at', with: '002055-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('もくもく会作成') + end + + it 'held_atが未記入だと新しいイベントは作成されない。' do + visit new_event_path + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('もくもく会作成') + end + end + + context 'edit→update' do + before do + login(event.user) + end + + it 'イベント(未来)を更新できる。トップページでそれを確認できる。' do + visit edit_event_path(event) + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '002055-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(event.user.name) + expect(page).to have_content('開催前') + + visit root_path + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(event.user.name) + end + + it 'イベント(過去)を更新できる。ただしトップページでそれは確認できない。"過去イベント"において確認できる。' do + visit edit_event_path(event) + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '002014-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(event.user.name) + expect(page).to have_content('開催済み') + + visit root_path + expect(page).not_to have_content('RUNTEQもくもく会') + expect(page).not_to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).not_to have_content(event.user.name) + + visit past_events_path + expect(page).to have_content('RUNTEQもくもく会') + expect(page).to have_content('渋谷にあるRUNTEQ教室でもくもく会') + expect(page).to have_content(event.user.name) + end + + it 'titleが未記入だと新しいイベントは作成されない。' do + visit edit_event_path(event) + fill_in 'Title', with: '' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '002055-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('もくもく会編集') + end + + it 'contentが未記入だと新しいイベントは作成されない。' do + visit edit_event_path(event) + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '' + fill_in 'Held at', with: '002055-10-02-1300' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('もくもく会編集') + end + + it 'held_atが未記入だと新しいイベントは作成されない。' do + visit edit_event_path(event) + fill_in 'Title', with: 'RUNTEQもくもく会' + fill_in 'Content', with: '渋谷にあるRUNTEQ教室でもくもく会' + fill_in 'Held at', with: '' + select '東京都', from: 'Prefecture' + click_button '登録' + + expect(page).to have_content('もくもく会編集') + end + end + end +end diff --git a/spec/system/followings_spec.rb b/spec/system/followings_spec.rb new file mode 100644 index 000000000..567aa2b17 --- /dev/null +++ b/spec/system/followings_spec.rb @@ -0,0 +1,85 @@ +require 'rails_helper' + +RSpec.describe "Followings", type: :system do + let(:user) { create :user } + let(:new_user) { create :user } + let(:event) { create :event } + let(:attended_event) { EventAttendance.create(user: user, event: event) } + + describe 'フォロー関係' do + context 'フォローする' do + it 'ユーザーは他のイベント参加者をフォローできる。' do + attended_event + login(new_user) + visit root_path + expect(page).to have_content(attended_event.event.title) + + click_link attended_event.event.title + click_link attended_event.user.name + + expect(page).to have_content('フォローする') + + click_link 'フォローする' + + expect(page).to have_content('フォローを外す') + end + + it 'ユーザーはイベント主催者をフォローできる。' do + login(new_user) + visit event_path(event) + expect(page).to have_content(event.title) + expect(page).to have_content('開催前') + + click_link event.user.name + + expect(page).to have_content('フォローする') + + click_link 'フォローする' + + expect(page).to have_content('フォローを外す') + end + end + + context 'フォローを解除する' do + it 'ユーザーは他のイベント参加者でフォロー登録している人を外すことができる' do + attended_event + login(new_user) + visit root_path + expect(page).to have_content(attended_event.event.title) + + click_link attended_event.event.title + click_link attended_event.user.name + + expect(page).to have_content('フォローする') + + click_link 'フォローする' + + expect(page).to have_content('フォローを外す') + + click_link 'フォローを外す' + + expect(page).to have_content('フォローする') + end + + it 'ユーザーはイベント主催者をフォローから外すことができる' do + + login(new_user) + visit event_path(event) + expect(page).to have_content(event.title) + expect(page).to have_content('開催前') + + click_link event.user.name + + expect(page).to have_content('フォローする') + + click_link 'フォローする' + + expect(page).to have_content('フォローを外す') + + click_link 'フォローを外す' + + expect(page).to have_content('フォローする') + end + end + end +end diff --git a/spec/system/profiles_spec.rb b/spec/system/profiles_spec.rb new file mode 100644 index 000000000..b36bc4655 --- /dev/null +++ b/spec/system/profiles_spec.rb @@ -0,0 +1,69 @@ +require 'rails_helper' + +RSpec.describe "Profiles", type: :system do + let(:user) { create :user, hobby: 'プログラミング', profile: 'プログラミングスクール「RUNTEQ」にて学び...' } + let(:other_user) { create :user, hobby: '読書', profile: '読書が好きで...' } + let(:new_user) { create :user } + let(:event) { create :event, user: user } + let(:attended_event) { EventAttendance.create(user: other_user, event: event) } + + describe 'profile関係' do + context 'profileを登録できる・更新できる' do + it 'ユーザーはhobby・profileを登録できる。' do + login(new_user) + visit mypage_profile_path + fill_in 'Hobby', with: '学習' + fill_in 'Profile', with: '今月からWebエンジニアを目指して...' + click_button '更新する' + + expect(page).to have_field 'Hobby', with: '学習' + expect(page).to have_field 'Profile', with: '今月からWebエンジニアを目指して...' + end + + it 'ユーザーはhobby・profileを更新できる。' do + login(user) + visit mypage_profile_path + expect(page).to have_field 'Hobby', with: user.hobby + expect(page).to have_field 'Profile', with: user.profile + + fill_in 'Hobby', with: '学習' + fill_in 'Profile', with: '今月からWebエンジニアを目指して...' + click_button '更新する' + + expect(page).to have_field 'Hobby', with: '学習' + expect(page).to have_field 'Profile', with: '今月からWebエンジニアを目指して...' + end + end + + context 'イベント主催者のプロフィールを閲覧できる' do + it 'イベント主催者の名前をクリックすると、イベント主催者のhobby・profileが閲覧できる。' do + event + login(new_user) + visit root_path + expect(page).to have_content(event.title) + + click_link event.user.name + + expect(page).to have_content(event.user.name) + expect(page).to have_content(event.user.hobby) + expect(page).to have_content(event.user.profile) + end + end + + context 'イベント参加者のプロフィールを閲覧できる' do + it 'イベント詳細ページから参加者の名前をクリックすると、対象ユーザーのhobby・profileが閲覧できる。' do + attended_event + login(new_user) + visit root_path + expect(page).to have_content(attended_event.event.title) + + click_link attended_event.event.title + click_link attended_event.user.name + + expect(page).to have_content(attended_event.user.name) + expect(page).to have_content(attended_event.user.hobby) + expect(page).to have_content(attended_event.user.profile) + end + end + end +end diff --git a/spec/system/sessions_spec.rb b/spec/system/sessions_spec.rb new file mode 100644 index 000000000..1c65032e8 --- /dev/null +++ b/spec/system/sessions_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' + +RSpec.describe "Sessions", type: :system do + let(:user) { create :user } + + describe 'session関係' do + it '登録されているユーザーでログインを行い、イベント一覧ページにリダイレクトされる。' do + visit login_path + fill_in 'email', with: user.email + fill_in 'password', with: 'password' + click_button 'ログイン' + + expect(page).to have_content('もくもく会を作る') + end + + it '登録されていないユーザーでログインを行うと、ログインページが表示される。' do + visit login_path + fill_in 'email', with: 'hogehogee4674@exmaple.com' + fill_in 'password', with: 'password' + click_button 'ログイン' + + expect(page).to have_content('Sign up') + expect(page).not_to have_content('I agree to the Terms of Service and Privacy Policy.') + end + end +end diff --git a/spec/system/users_spec.rb b/spec/system/users_spec.rb new file mode 100644 index 000000000..5ad69625c --- /dev/null +++ b/spec/system/users_spec.rb @@ -0,0 +1,58 @@ +require 'rails_helper' + +RSpec.describe "Users", type: :system do + describe 'Sign up関係' do + context '成功系' do + it 'Sign upを行い、ログイン処理を行うとイベント一覧ページにリダイレクトされる。' do + visit signup_path + fill_in 'Name', with: 'らんてくん' + fill_in 'Email', with: 'sample@example.com' + fill_in 'Password', with: 'password' + fill_in 'Password confirmation', with: 'password' + click_button '登録' + + visit login_path + fill_in 'email', with: 'sample@example.com' + fill_in 'password', with: 'password' + click_button 'ログイン' + + expect(page).to have_content('もくもく会を作る') + end + end + + context '失敗系' do + it 'passwordが未入力だと。Sign upページが表示される。' do + visit signup_path + fill_in 'Name', with: 'らんてくん' + fill_in 'Email', with: 'sample@example.com' + fill_in 'Password', with: '' + fill_in 'Password confirmation', with: 'password' + click_button '登録' + + expect(page).to have_content('I agree to the Terms of Service and Privacy Policy.') + end + + it 'password_confirmationが未入力だと。Sign upページが表示される。' do + visit signup_path + fill_in 'Name', with: 'らんてくん' + fill_in 'Email', with: 'sample@example.com' + fill_in 'Password', with: 'password' + fill_in 'Password confirmation', with: '' + click_button '登録' + + expect(page).to have_content('I agree to the Terms of Service and Privacy Policy.') + end + + it 'password, password_confirmationが未入力だと。Sign upページが表示される。' do + visit signup_path + fill_in 'Name', with: 'らんてくん' + fill_in 'Email', with: 'sample@example.com' + fill_in 'Password', with: '' + fill_in 'Password confirmation', with: '' + click_button '登録' + + expect(page).to have_content('I agree to the Terms of Service and Privacy Policy.') + end + end + end +end