diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..87096887d Binary files /dev/null and b/.DS_Store differ diff --git a/.node-version b/.node-version new file mode 100644 index 000000000..431076a94 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +16.16.0 diff --git a/Gemfile b/Gemfile index 059b8fabb..cbdaae178 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ ruby '3.0.2' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main' gem 'rails', '~> 6.1.4', '>= 6.1.4.1' # Use Puma as the app server -gem 'puma', '~> 5.0' +gem 'puma' # Use SCSS for stylesheets gem 'sass-rails', '>= 6' # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker @@ -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 diff --git a/Gemfile.lock b/Gemfile.lock index 4d19f0151..81f94aa24 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) @@ -180,7 +190,9 @@ GEM multi_json (1.15.0) multi_xml (0.6.0) multipart-post (2.1.1) - nio4r (2.5.8) + nio4r (2.5.9) + nokogiri (1.12.5-arm64-darwin) + racc (~> 1.4) nokogiri (1.12.5-x86_64-darwin) racc (~> 1.4) nokogiri (1.12.5-x86_64-linux) @@ -197,7 +209,7 @@ GEM ast (~> 2.4.1) pg (1.2.3) public_suffix (4.0.6) - puma (5.5.2) + puma (6.4.0) nio4r (~> 2.0) racc (1.6.0) rack (2.2.3) @@ -284,6 +296,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 +307,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,17 +335,25 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) + webdrivers (5.3.1) + nokogiri (~> 1.6) + rubyzip (>= 1.3.0) + selenium-webdriver (~> 4.0, < 4.11) webpacker (5.4.3) activesupport (>= 5.2) rack-proxy (>= 0.6.1) railties (>= 5.2) semantic_range (>= 2.3.0) + websocket (1.2.10) 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 + arm64-darwin-22 x86_64-darwin-19 x86_64-linux @@ -336,6 +361,7 @@ DEPENDENCIES aws-sdk-s3 bootsnap (>= 1.4.4) byebug + capybara (~> 3.23) draper enum_help factory_bot_rails @@ -346,7 +372,7 @@ DEPENDENCIES letter_opener_web listen (~> 3.3) pg - puma (~> 5.0) + puma rack-mini-profiler (~> 2.0) rails (~> 6.1.4, >= 6.1.4.1) rails-i18n @@ -356,11 +382,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 64d25a2f7..738aa515b 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,22 @@ ## 環境構築 ``` -$ bundle install +$ bundle install --without=production $ bin/rails db:setup $ yarn install +$ bin/webpack $ bin/rails s ``` ## 事業をエンジニアリングしよう提案編の回答は以下に記述してください +①選択した事業側の課題 +直近一年間で、2回以上もくもく会に参加してくれた人は利用者全体の1%のみ。もくもく会で気の合う仲間を見つけられなかったのではないか? + +②提出内容 +もくもく会にタグの設定と絞り込みができるようにする。これにより、同じ目的を持った人たちが集まりやすいようにする。 +また、開催場所の検索と絞り込みもできるようにする。これにより、ユーザーが行きやすいイベントを簡単に見つけられるようにする。 + +③実装方針 +・もくもく会作成時にタグを設定できるようにする。 +・ヘッダーの検索フォームにタグと開催地の条件を追加する。 +・もくもく会一覧画面で、イベントに設定されているタグ又は開催地をクリックすると、それで絞り込みできるようにする。 \ No newline at end of file diff --git a/app/.DS_Store b/app/.DS_Store new file mode 100644 index 000000000..85a555b07 Binary files /dev/null and b/app/.DS_Store differ diff --git a/app/assets/.DS_Store b/app/assets/.DS_Store new file mode 100644 index 000000000..1b44e0509 Binary files /dev/null and b/app/assets/.DS_Store differ diff --git a/app/assets/stylesheets/.DS_Store b/app/assets/stylesheets/.DS_Store new file mode 100644 index 000000000..9216bd196 Binary files /dev/null and b/app/assets/stylesheets/.DS_Store differ diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 586f4fd9d..e11538d69 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -1 +1 @@ -@import 'theme.css'; +@import 'theme.css'; \ No newline at end of file diff --git a/app/controllers/mypage/profiles_controller.rb b/app/controllers/mypage/profiles_controller.rb index 966f90960..18b0008f5 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, :hobby, :profile) end end diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb new file mode 100644 index 000000000..4856f8237 --- /dev/null +++ b/app/controllers/relationships_controller.rb @@ -0,0 +1,11 @@ +class RelationshipsController < ApplicationController + def create + current_user.follow(params[:user_id]) + redirect_to user_path(params[:user_id]) + end + + def destroy + current_user.unfollow(params[:user_id]) + redirect_to user_path(params[:user_id]) + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 136d60eba..f355c6e2f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -5,6 +5,22 @@ def new @user = User.new end + def show + @user = User.find(params[:id]) + end + + def follow + user = User.find(params[:id]) + current_user.follow(user) + redirect_to user + end + + def unfollow + user = User.find(params[:id]) + current_user.unfollow(user) + redirect_to user + end + def create @user = User.new(user_params) if @user.save diff --git a/app/decorators/relationship_decorator.rb b/app/decorators/relationship_decorator.rb new file mode 100644 index 000000000..bb203bbee --- /dev/null +++ b/app/decorators/relationship_decorator.rb @@ -0,0 +1,13 @@ +class RelationshipDecorator < 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/javascript/.DS_Store b/app/javascript/.DS_Store new file mode 100644 index 000000000..7e4ac4bd6 Binary files /dev/null and b/app/javascript/.DS_Store differ diff --git a/app/javascript/channels/.DS_Store b/app/javascript/channels/.DS_Store new file mode 100644 index 000000000..f2e488e7c Binary files /dev/null and b/app/javascript/channels/.DS_Store differ diff --git a/app/models/relationship.rb b/app/models/relationship.rb new file mode 100644 index 000000000..c80d25be7 --- /dev/null +++ b/app/models/relationship.rb @@ -0,0 +1,4 @@ +class Relationship < ApplicationRecord + belongs_to :follower, class_name: "User" + belongs_to :followed, class_name: "User" +end diff --git a/app/models/user.rb b/app/models/user.rb index 894c04a65..f522685a0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,6 +13,14 @@ class User < ApplicationRecord has_many :user_notification_timings, dependent: :destroy has_many :notification_timings, through: :user_notification_timings has_one_attached :avatar + has_many :active_relationships, class_name: "Relationship", + foreign_key: "follower_id", + dependent: :destroy + has_many :passive_relationships, class_name: "Relationship", + foreign_key: "followed_id", + dependent: :destroy + has_many :following, through: :active_relationships, source: :followed + has_many :followers, through: :passive_relationships, source: :follower validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } @@ -76,4 +84,16 @@ def allow_attended_to_event_notification? def allow_liked_event_notification? notification_timings.liked_event.present? end + + def follow(user_id) + active_relationships.create(followed_id: user_id) + end + + def unfollow(user_id) + active_relationships.find_by(followed_id: user_id).destroy + end + + def following?(user) + following.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 @@
  • <%= image_tag attendee.decorate.avatar, class: 'rounded-circle avatar-md' %> - <%= attendee.name %> + <%= link_to attendee.name, user_path(attendee) %>
  • diff --git a/app/views/events/_event.html.erb b/app/views/events/_event.html.erb index 4725d15fa..a162db1e8 100644 --- a/app/views/events/_event.html.erb +++ b/app/views/events/_event.html.erb @@ -23,7 +23,7 @@
    <%= image_tag event.user.decorate.avatar, class: 'avatar-sm rounded-circle' %> - <%= event.user.name %> + <%= link_to event.user.name, user_path(event.user) %>
    <% if logged_in? %>
    diff --git a/app/views/events/show.html.erb b/app/views/events/show.html.erb index 6c7ac91d2..6a534dc7d 100644 --- a/app/views/events/show.html.erb +++ b/app/views/events/show.html.erb @@ -57,7 +57,7 @@
    <%= image_tag @event.user.decorate.avatar, class: 'rounded-circle avatar-lg' %> - <%= @event.user.name %> + <%= link_to @event.user.name, user_path(@event.user) %>
    イベント数 diff --git a/app/views/mypage/profiles/show.html.erb b/app/views/mypage/profiles/show.html.erb index 31e19038f..a9b175622 100644 --- a/app/views/mypage/profiles/show.html.erb +++ b/app/views/mypage/profiles/show.html.erb @@ -38,6 +38,14 @@ <%= f.label :name, class: 'form-label' %> <%= f.text_field :name, class: 'form-control', placeholder: 'らんてくん' %>
    +
    + <%= f.label :hobby, class: 'form-label' %> + <%= f.text_field :hobby, class: 'form-control', placeholder: '趣味' %> +
    +
    + <%= f.label :profile, class: 'form-label' %> + <%= f.text_field :profile, class: 'form-control', placeholder: 'プロフィール' %> +
    <%= f.submit class: 'btn btn-primary' %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb new file mode 100644 index 000000000..c649453fb --- /dev/null +++ b/app/views/users/show.html.erb @@ -0,0 +1,38 @@ +
    +
    +
    +
    +
    +
    +
    + <%= image_tag @user.decorate.avatar, class: 'avatar-lg rounded-circle' %> +
    +

    + <%= @user.name %> +

    + <% if current_user != @user %> + <% if current_user.following?(@user) %> + <%= link_to "フォローを外す", user_relationships_path(@user.id), method: :delete, class: "btn btn-danger" %> + <% else %> + <%= link_to "フォローする", user_relationships_path(@user.id), method: :post, class:"btn btn-success" %> + <% end %> + <% end %> +
    +
    +
    +
    +
    + プロフィール +
    +
    +

    名前

    +

    <%= @user.name %>

    +

    趣味

    +

    <%= @user.hobby %>

    +

    自己紹介

    +

    <%= @user.profile %>

    +
    +
    +
    +
    +
    diff --git a/config/routes.rb b/config/routes.rb index 018ec9d39..969cf02f2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,7 +9,10 @@ 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 + resource :relationships, only: [:create, :destroy] + end + resources :events do collection do get :future diff --git a/db/migrate/20231020113443_add_details_to_users.rb b/db/migrate/20231020113443_add_details_to_users.rb new file mode 100644 index 000000000..b70580e9e --- /dev/null +++ b/db/migrate/20231020113443_add_details_to_users.rb @@ -0,0 +1,6 @@ +class AddDetailsToUsers < ActiveRecord::Migration[6.1] + def change + add_column :users, :hobby, :string + add_column :users, :profile, :string + end +end diff --git a/db/migrate/20231023124843_create_relationships.rb b/db/migrate/20231023124843_create_relationships.rb new file mode 100644 index 000000000..049aecfa0 --- /dev/null +++ b/db/migrate/20231023124843_create_relationships.rb @@ -0,0 +1,14 @@ +class CreateRelationships < ActiveRecord::Migration[6.1] + def change + create_table :relationships do |t| + t.integer :follower_id + t.integer :followed_id + + t.timestamps + end + + add_index :relationships, :follower_id + add_index :relationships, :followed_id + add_index :relationships, [:follower_id, :followed_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 01c4634ac..a8cdef73e 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_10_23_124843) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false @@ -109,6 +109,16 @@ t.datetime "updated_at", precision: 6, null: false end + create_table "relationships", 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_relationships_on_followed_id" + t.index ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true + t.index ["follower_id"], name: "index_relationships_on_follower_id" + end + create_table "user_notification_timings", force: :cascade do |t| t.integer "user_id", null: false t.integer "notification_timing_id", 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/package.json b/package.json index 580a5e8a2..195938433 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "@rails/activestorage": "^6.0.0", "@rails/ujs": "^6.0.0", "@rails/webpacker": "5.4.3", - "jquery": "^3.6.0", + "bootstrap": "^5.3.2", + "jquery": "^3.7.1", + "jquery-ui": "^1.13.2", + "tag-it": "^2.0.0", "webpack": "^4.46.0", "webpack-cli": "^3.3.12" }, diff --git a/spec/.DS_Store b/spec/.DS_Store new file mode 100644 index 000000000..59400cc58 Binary files /dev/null and b/spec/.DS_Store differ 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..de02874fb --- /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 diff --git a/yarn.lock b/yarn.lock index 7f5cd0cd5..fa757fceb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1561,6 +1561,11 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +bootstrap@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.2.tgz#97226583f27aae93b2b28ab23f4c114757ff16ae" + integrity sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3854,10 +3859,17 @@ jest-worker@^26.5.0: merge-stream "^2.0.0" supports-color "^7.0.0" -jquery@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" - integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw== +jquery-ui@^1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.2.tgz#de03580ae6604773602f8d786ad1abfb75232034" + integrity sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q== + dependencies: + jquery ">=1.8.0 <4.0.0" + +"jquery@>=1.8.0 <4.0.0", jquery@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" + integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== js-tokens@^4.0.0: version "4.0.0" @@ -6427,6 +6439,11 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" +tag-it@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tag-it/-/tag-it-2.0.0.tgz#7b618759df840fb90294a54257a947fa2b148542" + integrity sha512-tyAsJbKfIk28TgcgjH7aK055HEzNPElZQ4d41YiNyfl9vjnDRJoK5rl46inzNNNt/oCBl1B1qy0zu7M6DKh9Rw== + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"