diff --git a/.rubocop.yml b/.rubocop.yml
index 8b2d084092d..b6c222ec208 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -8,10 +8,6 @@ inherit_gem:
- config/rubocop.yml
- config/rails.yml
-Metrics/BlockLength:
- Exclude:
- - test/**/*
-
Metrics/ClassLength:
Exclude:
- test/**/*
@@ -20,14 +16,16 @@ Metrics/ClassLength:
- app/controllers/users_controller.rb
- app/mailers/activity_mailer.rb
- app/models/event.rb
- - app/models/notification.rb
- - app/models/notification_facade.rb
- app/models/practice.rb
- app/models/user.rb
- app/models/product.rb
- app/models/report.rb
- app/notifiers/activity_notifier.rb
+Metrics/ModuleLength:
+ Exclude:
+ - app/decorators/user_decorator.rb
+
AllCops:
Exclude:
- '**/templates/**/*'
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 77c71421814..ba87dd8713a 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -103,9 +103,3 @@ Style/HashSyntax:
- 'test/system/sign_up_test.rb'
- 'test/system/user/products_test.rb'
- 'test/test_helper.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Style/RedundantFreeze:
- Exclude:
- - 'app/models/link_checker/extractor.rb'
diff --git a/app/controllers/api/products/self_assigned_controller.rb b/app/controllers/api/products/self_assigned_controller.rb
index da9dc3c7a82..72f2d178149 100644
--- a/app/controllers/api/products/self_assigned_controller.rb
+++ b/app/controllers/api/products/self_assigned_controller.rb
@@ -7,13 +7,15 @@ def index
@target = 'self_assigned_all' unless target_allowlist.include?(@target)
@products = case @target
when 'self_assigned_all'
- Product.self_assigned_product(current_user.id)
+ Product.unhibernated_user_products
+ .self_assigned_product(current_user.id)
.unchecked
.list
.order_for_self_assigned_list
.page(params[:page])
when 'self_assigned_no_replied'
- Product.self_assigned_no_replied_products(current_user.id)
+ Product.unhibernated_user_products
+ .self_assigned_no_replied_products(current_user.id)
.unchecked
.list
.page(params[:page])
diff --git a/app/decorators/user_decorator.rb b/app/decorators/user_decorator.rb
index 1dbe776f5c5..8ffcea64805 100644
--- a/app/decorators/user_decorator.rb
+++ b/app/decorators/user_decorator.rb
@@ -1,59 +1,13 @@
# frozen_string_literal: true
module UserDecorator
+ include Role
+ include Retire
+
def twitter_url
"https://twitter.com/#{twitter_account}"
end
- def roles
- role_list = [
- { role: 'retired', value: retired? },
- { role: 'hibernationed', value: hibernated? },
- { role: 'admin', value: admin? },
- { role: 'mentor', value: mentor? },
- { role: 'adviser', value: adviser? },
- { role: 'graduate', value: graduated? },
- { role: 'trainee', value: trainee? }
- ]
- roles = role_list.find_all { |v| v[:value] }
- .map { |v| v[:role] }
- roles << :student if roles.empty?
-
- roles
- end
-
- def primary_role
- roles.first
- end
-
- def staff_roles
- staff_roles = [
- { role: '管理者', value: admin? },
- { role: 'メンター', value: mentor? },
- { role: 'アドバイザー', value: adviser? }
- ]
- staff_roles.find_all { |v| v[:value] }
- .map { |v| v[:role] }
- .join('、')
- end
-
- def roles_to_s
- return '' if roles.empty?
-
- roles = [
- { role: '退会ユーザー', value: retired? },
- { role: '休会ユーザー', value: hibernated? },
- { role: '管理者', value: admin? },
- { role: 'メンター', value: mentor? },
- { role: 'アドバイザー', value: adviser? },
- { role: '卒業生', value: graduated? },
- { role: '研修生', value: trainee? }
- ]
- roles.find_all { |v| v[:value] }
- .map { |v| v[:role] }
- .join('、')
- end
-
def icon_title
["#{login_name} (#{name})", staff_roles].reject(&:blank?)
.join(': ')
@@ -120,4 +74,8 @@ def address
country_name
end
end
+
+ def hibernation_days
+ ActiveSupport::Duration.build(Time.zone.now - hibernated_at).in_days.floor if hibernated_at?
+ end
end
diff --git a/app/decorators/user_decorator/retire.rb b/app/decorators/user_decorator/retire.rb
new file mode 100644
index 00000000000..55d4b404242
--- /dev/null
+++ b/app/decorators/user_decorator/retire.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module UserDecorator
+ module Retire
+ def retire_countdown
+ ActiveSupport::Duration.build(scheduled_retire_at - Time.current) if hibernated_at?
+ end
+
+ def retire_deadline
+ countdown =
+ if retire_countdown.in_hours < 1
+ "#{retire_countdown.in_minutes.floor}分"
+ elsif retire_countdown.in_hours < 24
+ "#{retire_countdown.in_hours.floor}時間"
+ else
+ "#{retire_countdown.in_days.floor}日"
+ end
+
+ "#{l scheduled_retire_at} (自動退会まであと#{countdown})"
+ end
+
+ def countdown_danger_tag
+ retire_countdown.in_days <= 7 ? 'is-danger' : ''
+ end
+ end
+end
diff --git a/app/decorators/user_decorator/role.rb b/app/decorators/user_decorator/role.rb
new file mode 100644
index 00000000000..b27b1d44ea6
--- /dev/null
+++ b/app/decorators/user_decorator/role.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module UserDecorator
+ module Role
+ def roles
+ role_list = [
+ { role: 'retired', value: retired? },
+ { role: 'hibernationed', value: hibernated? },
+ { role: 'admin', value: admin? },
+ { role: 'mentor', value: mentor? },
+ { role: 'adviser', value: adviser? },
+ { role: 'graduate', value: graduated? },
+ { role: 'trainee', value: trainee? }
+ ]
+ roles = role_list.find_all { |v| v[:value] }
+ .map { |v| v[:role] }
+ roles << :student if roles.empty?
+
+ roles
+ end
+
+ def primary_role
+ roles.first
+ end
+
+ def staff_roles
+ staff_roles = [
+ { role: '管理者', value: admin? },
+ { role: 'メンター', value: mentor? },
+ { role: 'アドバイザー', value: adviser? }
+ ]
+ staff_roles.find_all { |v| v[:value] }
+ .map { |v| v[:role] }
+ .join('、')
+ end
+
+ def roles_to_s
+ return '' if roles.empty?
+
+ roles = [
+ { role: '退会ユーザー', value: retired? },
+ { role: '休会ユーザー', value: hibernated? },
+ { role: '管理者', value: admin? },
+ { role: 'メンター', value: mentor? },
+ { role: 'アドバイザー', value: adviser? },
+ { role: '卒業生', value: graduated? },
+ { role: '研修生', value: trainee? }
+ ]
+ roles.find_all { |v| v[:value] }
+ .map { |v| v[:role] }
+ .join('、')
+ end
+ end
+end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index ab0f8f4da3b..74586db4dcd 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -16,8 +16,8 @@ def searchable_url(searchable)
document = searchable.commentable_type.constantize.find(searchable.commentable_id)
"#{polymorphic_url(document)}#comment_#{searchable.id}"
elsif searchable.instance_of?(Answer) || searchable.instance_of?(CorrectAnswer)
- document = searchable.question
- polymorphic_url(document).to_s
+ document = Question.find(searchable.question.id)
+ "#{polymorphic_url(document)}#answer_#{searchable.id}"
else
polymorphic_url(searchable)
end
diff --git a/app/javascript/components/Searchable.jsx b/app/javascript/components/Searchable.jsx
new file mode 100644
index 00000000000..3be60d4584f
--- /dev/null
+++ b/app/javascript/components/Searchable.jsx
@@ -0,0 +1,173 @@
+import React from 'react'
+import dayjs from 'dayjs'
+import ja from 'dayjs/locale/ja'
+dayjs.locale(ja)
+
+export default function Searchable({ searchable, word }) {
+ const roleClass = `is-${searchable.primary_role}`
+ const searchableClass = searchable.wip
+ ? `is-wip is-${searchable.model_name}`
+ : `is-${searchable.model_name}`
+
+ const userUrl = `/users/${searchable.user_id}`
+ const documentAuthorUserUrl = `/users/${searchable.document_author_id}`
+ const talkUrl = `/talks/${searchable.talk_id}`
+
+ const updatedAt = dayjs(searchable.updated_at).format(
+ 'YYYY年MM月DD日(dd) HH:mm'
+ )
+ const canDisplayTalk = searchable.model_name === 'user' && searchable.talk_id
+ const currentUser = window.currentUser
+
+ const labelContent =
+ searchable.model_name === 'regular_event' ? (
+
+ 定期
+
+ イベント
+
+ ) : searchable.model_name === 'event' ? (
+
+ 特別
+
+ イベント
+
+ ) : searchable.model_name === 'practice' ? (
+
+ プラク
+
+ ティス
+
+ ) : (
+
+ {searchable.model_name_with_i18n}
+
+ )
+
+ const badgeContent = searchable.wip ? (
+
+ WIP
+
+ ) : searchable.is_comment_or_answer ? (
+
+ コメント
+
+ ) : searchable.is_user ? (
+
+ ユーザー
+
+ ) : (
+ ''
+ )
+
+ const summary = () => {
+ const wordsPattern = word
+ .trim()
+ .replaceAll(/[.*+?^=!:${}()|[\]/\\]/g, '\\$&')
+ .replaceAll(/\s+/g, '|')
+ const pattern = new RegExp(wordsPattern, 'gi')
+ if (word) {
+ return searchable.summary.replaceAll(
+ pattern,
+ `$&`
+ )
+ } else {
+ return searchable.summary
+ }
+ }
+
+ return (
+
+
+ {searchable.is_user && (
+
+ )}
+ {!searchable.is_user && (
+
{labelContent}
+ )}
+
+
+
+
+
+
+ {!['practice', 'page', 'user'].includes(
+ searchable.model_name
+ ) && (
+
+ )}
+
+ {searchable.is_comment_or_answer && (
+
+ )}
+ {currentUser.roles.includes('admin') && canDisplayTalk && (
+
+ )}
+
+
+
+
+
+
+ )
+}
diff --git a/app/javascript/components/Searchables.jsx b/app/javascript/components/Searchables.jsx
new file mode 100644
index 00000000000..d192cb3430b
--- /dev/null
+++ b/app/javascript/components/Searchables.jsx
@@ -0,0 +1,79 @@
+import React from 'react'
+import useSWR from 'swr'
+import usePage from './hooks/usePage'
+import Pagination from './Pagination'
+import LoadingListPlaceholder from './LoadingListPlaceholder'
+import Searchable from './Searchable'
+import fetcher from '../fetcher'
+
+export default function Searchables({ word }) {
+ const per = 50
+ const { page, setPage } = usePage()
+ const params = new URLSearchParams(location.search)
+ const url = `/api/searchables.json?${params}`
+ const { data, error } = useSWR(url, fetcher)
+
+ if (error) {
+ console.warn(error)
+ return failed to load
+ }
+
+ return (
+
+ {!data && (
+
+
+
+ )}
+
+ {data?.searchables.length === 0 && (
+
+
+
+
+
+
+ {word}に一致する情報は見つかりませんでした。
+
+
+
+ )}
+
+ {data && data.searchables.length > 0 && (
+
+ {data.total_pages > 1 && (
+
+ )}
+
+ {data.searchables.map((searchable) => (
+
+ ))}
+
+ {data.total_pages > 1 && (
+
+ )}
+
+ )}
+
+ )
+}
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 9fde0cc59c7..9263c366460 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -34,7 +34,6 @@ import '../courses-practices.js'
import '../no_learn.js'
import '../survey_question.js'
import '../survey.js'
-import '../searchables.js'
import '../niconico_calendar.js'
import '../mentor-mode.js'
import '../bookmark.js'
diff --git a/app/javascript/searchables.js b/app/javascript/searchables.js
deleted file mode 100644
index 0932779df8e..00000000000
--- a/app/javascript/searchables.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import Vue from 'vue'
-import Searchables from 'searchables.vue'
-
-document.addEventListener('DOMContentLoaded', () => {
- const selector = '#js-searchables'
- const searchables = document.querySelector(selector)
- if (searchables) {
- const documentType = searchables.getAttribute('document_type')
- const word = searchables.getAttribute('word')
- new Vue({
- render: (h) =>
- h(Searchables, {
- props: { documentType: documentType, word: word }
- })
- }).$mount(selector)
- }
-})
diff --git a/app/javascript/stylesheets/application/blocks/user/_a-list-item-badge.sass b/app/javascript/stylesheets/application/blocks/user/_a-list-item-badge.sass
index 3ecbbd833c7..e6abdbe57cc 100644
--- a/app/javascript/stylesheets/application/blocks/user/_a-list-item-badge.sass
+++ b/app/javascript/stylesheets/application/blocks/user/_a-list-item-badge.sass
@@ -20,7 +20,7 @@
&.is-unread
background-color: var(--danger)
color: var(--reversal-text)
- &.is-serchable
+ &.is-searchable
border: solid 1px var(--muted-text)
color: var(--muted-text)
&.is-ended
diff --git a/app/javascript/stylesheets/application/blocks/user/_user-metas.sass b/app/javascript/stylesheets/application/blocks/user/_user-metas.sass
index 43e85af80e5..a97f747298a 100644
--- a/app/javascript/stylesheets/application/blocks/user/_user-metas.sass
+++ b/app/javascript/stylesheets/application/blocks/user/_user-metas.sass
@@ -53,3 +53,8 @@
margin-top: 0
*:last-child
margin-bottom: 0
+
+.user-metas__item-value-text
+ &.is-danger
+ color: $danger
+ font-weight: 700
diff --git a/app/javascript/stylesheets/atoms/_a-list-item-badge.sass b/app/javascript/stylesheets/atoms/_a-list-item-badge.sass
index 3ecbbd833c7..e6abdbe57cc 100644
--- a/app/javascript/stylesheets/atoms/_a-list-item-badge.sass
+++ b/app/javascript/stylesheets/atoms/_a-list-item-badge.sass
@@ -20,7 +20,7 @@
&.is-unread
background-color: var(--danger)
color: var(--reversal-text)
- &.is-serchable
+ &.is-searchable
border: solid 1px var(--muted-text)
color: var(--muted-text)
&.is-ended
diff --git a/app/models/cache.rb b/app/models/cache.rb
index b9656ab5565..b2353db6b19 100644
--- a/app/models/cache.rb
+++ b/app/models/cache.rb
@@ -34,7 +34,7 @@ def delete_unassigned_product_count
def self_assigned_no_replied_product_count(user_id)
Rails.cache.fetch("#{user_id}-self_assigned_no_replied_product_count") do
- Product.self_assigned_no_replied_products(user_id).unchecked.count
+ Product.unhibernated_user_products.self_assigned_no_replied_products(user_id).unchecked.count
end
end
diff --git a/app/models/learning_cache_destroyer.rb b/app/models/learning_cache_destroyer.rb
index 47b9ff43df8..e62c56939a1 100644
--- a/app/models/learning_cache_destroyer.rb
+++ b/app/models/learning_cache_destroyer.rb
@@ -4,5 +4,6 @@ class LearningCacheDestroyer
def call(payload)
user = payload[:user]
Rails.cache.delete "/model/user/#{user.id}/completed_percentage"
+ Rails.logger.info "[LearningCacheDestroyer] Cache destroyed for user #{user.id}"
end
end
diff --git a/app/models/link_checker/extractor.rb b/app/models/link_checker/extractor.rb
index 36c082935a1..cdff6ff2edd 100644
--- a/app/models/link_checker/extractor.rb
+++ b/app/models/link_checker/extractor.rb
@@ -2,7 +2,7 @@
module LinkChecker
module Extractor
- MARKDOWN_LINK_REGEXP = %r{\[(.*?)\]\((#{URI::DEFAULT_PARSER.make_regexp}|/.*?)\)}.freeze
+ MARKDOWN_LINK_REGEXP = %r{\[(.*?)\]\((#{URI::DEFAULT_PARSER.make_regexp}|/.*?)\)}
module_function
diff --git a/app/models/user.rb b/app/models/user.rb
index 9aeb1475626..db2d4fd8283 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -770,6 +770,10 @@ def delete_and_assign_new_organizer
organizers.each(&:delete_and_assign_new)
end
+ def scheduled_retire_at
+ hibernated_at&.advance(months: 6)
+ end
+
private
def password_required?
diff --git a/app/views/articles/_form.html.slim b/app/views/articles/_form.html.slim
index f8e44c94a9e..bfa53ca8ddb 100644
--- a/app/views/articles/_form.html.slim
+++ b/app/views/articles/_form.html.slim
@@ -37,9 +37,11 @@
.form-item
.row
.col-md-6.col-xs-12
- = f.label :tag_list, 'タグを入力してください(カンマ区切り)',
+ = f.label :tag_list, 'タグを入力してください',
class: 'a-form-label'
= render 'tags_input', taggable: article
+ .a-form-help
+ p 入力してエンターキーを押すとタグになります(スペースは入力できません)。
.form-item
.row
diff --git a/app/views/articles/index.html.slim b/app/views/articles/index.html.slim
index adc8b8a1040..a8a24cf14f8 100644
--- a/app/views/articles/index.html.slim
+++ b/app/views/articles/index.html.slim
@@ -19,6 +19,7 @@ description: 'オンラインプログラミングフィヨルドブートキャ
.articles
.articles__body
.container.is-xl
+ = paginate @articles
.articles__items
.row
- @articles.each do |article|
diff --git a/app/views/courses/books/index.html.slim b/app/views/courses/books/index.html.slim
index f7277ca4498..e676bbec496 100644
--- a/app/views/courses/books/index.html.slim
+++ b/app/views/courses/books/index.html.slim
@@ -14,14 +14,13 @@ header.page-header
i.fas.fa-plus
| 参考書籍登録
= render 'courses/tabs'
-hr.a-border
- .page-body
- .container.is-md
- nav.pill-nav
- .container
- ul.pill-nav__items
- li.pill-nav__item
- = link_to '全て', course_books_path, class: "pill-nav__item-link #{params[:status] == 'mustread' ? '' : 'is-active'}"
- li.pill-nav__item
- = link_to '必読', course_books_path(status: 'mustread'), class: "pill-nav__item-link #{params[:status] == 'mustread' ? 'is-active' : ''}"
+.page-body
+ .container.is-md
+ nav.pill-nav
+ .container
+ ul.pill-nav__items
+ li.pill-nav__item
+ = link_to '全て', course_books_path, class: "pill-nav__item-link #{params[:status] == 'mustread' ? '' : 'is-active'}"
+ li.pill-nav__item
+ = link_to '必読', course_books_path(status: 'mustread'), class: "pill-nav__item-link #{params[:status] == 'mustread' ? 'is-active' : ''}"
div(data-vue="CourseBooks" data-vue-is-admin:boolean="#{current_user.admin?}" data-vue-is-mentor:boolean="#{current_user.mentor?}" data-vue-course:json="#{@course.to_json}")
diff --git a/app/views/products/_tabs.html.slim b/app/views/products/_tabs.html.slim
index f61827962d7..89414700d14 100644
--- a/app/views/products/_tabs.html.slim
+++ b/app/views/products/_tabs.html.slim
@@ -15,7 +15,7 @@
= Cache.unassigned_product_count
li.page-tabs__item
= link_to products_self_assigned_index_path, class: "page-tabs__item-link #{current_link(/^products-self_assigned-index/)}" do
- | 自分の担当 (#{Product.self_assigned_product(current_user.id).unchecked.size})
+ | 自分の担当 (#{Product.unhibernated_user_products.self_assigned_product(current_user.id).unchecked.size})
- if Cache.self_assigned_no_replied_product_count(current_user.id).positive?
.page-tabs__item-count.a-notification-count.is-only-mentor
= Cache.self_assigned_no_replied_product_count(current_user.id)
diff --git a/app/views/reports/index.html.slim b/app/views/reports/index.html.slim
index b9cd2abb6a3..4a45d0bced7 100644
--- a/app/views/reports/index.html.slim
+++ b/app/views/reports/index.html.slim
@@ -14,4 +14,5 @@ header.page-header
= render 'reports/tabs'
-= react_component('Reports', all: true, practices: current_user.practices)
+- practices = current_user.practices.as_json(only: %i[id title])
+= react_component('Reports', all: true, practices: practices)
diff --git a/app/views/searchables/index.html.slim b/app/views/searchables/index.html.slim
index aebbade6f68..0a6e60d2b3d 100644
--- a/app/views/searchables/index.html.slim
+++ b/app/views/searchables/index.html.slim
@@ -7,4 +7,4 @@ header.page-header
h2.page-header__title
= title
hr.a-border
-#js-searchables(document_type="#{params[:document_type]}" word="#{params[:word]}")
+ = react_component('Searchables', word: params[:word].to_s)
diff --git a/app/views/users/_hibernation_info.html.slim b/app/views/users/_hibernation_info.html.slim
index 82b5b5279dd..d8508019a53 100644
--- a/app/views/users/_hibernation_info.html.slim
+++ b/app/views/users/_hibernation_info.html.slim
@@ -6,6 +6,15 @@
.user-metas__item-label 最後に休会した日時
.user-metas__item-value
= l user.hibernated_at
+ .user-metas__item
+ .user-metas__item-label 休会期限日時
+ .user-metas__item-value
+ - if !user.auto_retire
+ span.user-metas__item-value-text
+ | 企業都合休会中(#{user.hibernation_days}日)
+ - else
+ span.user-metas__item-value-text(class=user.countdown_danger_tag)
+ = user.retire_deadline
- user.hibernations.each_with_index do |hibernation, i|
- number = i + 1
.user-metas__items
diff --git a/app/views/users/_metas.html.slim b/app/views/users/_metas.html.slim
index 6e7d6597c0c..86be86806ec 100644
--- a/app/views/users/_metas.html.slim
+++ b/app/views/users/_metas.html.slim
@@ -18,6 +18,14 @@
| コース
.user-metas__item-value
= link_to user.course.title, course_practices_path(user.course), target: '_blank', rel: 'noopener'
+ .user-metas__item
+ .user-metas__item-label
+ | マシンのOS
+ .user-metas__item-value
+ - if user.os
+ = t("activerecord.enums.user.os.#{user.os}")
+ - else
+ | 回答なし
.user-metas__item
.user-metas__item-label
| 日報
diff --git a/app/views/users/_user_secret_attributes.html.slim b/app/views/users/_user_secret_attributes.html.slim
index d35917a9f7e..0a85a06e172 100644
--- a/app/views/users/_user_secret_attributes.html.slim
+++ b/app/views/users/_user_secret_attributes.html.slim
@@ -33,14 +33,6 @@
= t("activerecord.enums.user.job.#{user.job}")
- else
| 回答なし
- .user-metas__item
- .user-metas__item-label
- | マシンのOS
- .user-metas__item-value
- - if user.os
- = t("activerecord.enums.user.os.#{user.os}")
- - else
- | 回答なし
.user-metas__item
.user-metas__item-label
| 経験
diff --git a/app/views/users/form/_os.html.slim b/app/views/users/form/_os.html.slim
index afa3531639b..88a04b84ea5 100644
--- a/app/views/users/form/_os.html.slim
+++ b/app/views/users/form/_os.html.slim
@@ -1,5 +1,9 @@
.form-item
= f.label :os, '学習に使うマシン・OS を選択してください', class: 'a-form-label is-required'
+ .a-form-help.mb-4
+ p
+ | この情報は他のフィヨルドブートキャンプ参加者に公開されます。
+ | 学習に使うマシン・OS が変わりましたら、この情報を更新してください。
.form-item__groups
.form-item-group
.form-item-group__header
diff --git a/app/views/welcome/law.html.slim b/app/views/welcome/law.html.slim
index 3fc1137701c..d541463ceb2 100644
--- a/app/views/welcome/law.html.slim
+++ b/app/views/welcome/law.html.slim
@@ -30,7 +30,7 @@ header.welcome-page-header
| ※この電話番号からのご利用方法お問い合わせや営業はお受けできません。
tr
th E-mail
- td info@fjord.jp
+ td info@lokka.jp
tr
th 役務の提供の時期
td
diff --git a/bin/lint b/bin/lint
index 6c9e377111e..4ee9a6df08c 100755
--- a/bin/lint
+++ b/bin/lint
@@ -1,5 +1,6 @@
#!/bin/bash
+set -e
bundle exec rubocop -a
bundle exec slim-lint app/views -c config/slim_lint.yml
bin/yarn lint
diff --git a/config/slim_lint.yml b/config/slim_lint.yml
index 6f95deb3a15..8e1ea7b0fb1 100644
--- a/config/slim_lint.yml
+++ b/config/slim_lint.yml
@@ -8,7 +8,6 @@ linters:
enabled: true
ignored_cops:
- Layout/ArgumentAlignment
- - Layout/ArrayAlignment
- Layout/BlockAlignment
- Layout/EmptyLineAfterGuardClause
- Layout/EndAlignment
@@ -22,7 +21,6 @@ linters:
- Layout/MultilineArrayBraceLayout
- Layout/MultilineAssignmentLayout
- Layout/MultilineHashBraceLayout
- - Layout/MultilineMethodCallBraceLayout
- Layout/MultilineMethodCallIndentation
- Layout/MultilineMethodDefinitionBraceLayout
- Layout/MultilineOperationIndentation
diff --git a/db/fixtures/discord_profiles.yml b/db/fixtures/discord_profiles.yml
index c29560d2acc..0197813a856 100644
--- a/db/fixtures/discord_profiles.yml
+++ b/db/fixtures/discord_profiles.yml
@@ -225,6 +225,31 @@ discord_profile_kyuukai:
account_name:
times_url:
+discord_profile_autoretire-within-1-hour:
+ user: autoretire-within-1-hour
+ account_name:
+ times_url:
+
+discord_profile_autoretire-within-24-hour:
+ user: autoretire-within-24-hour
+ account_name:
+ times_url:
+
+discord_profile_autoretire-within-1-week:
+ user: autoretire-within-1-week
+ account_name:
+ times_url:
+
+discord_profile_autoretire-over-1-week:
+ user: autoretire-over-1-week
+ account_name:
+ times_url:
+
+discord_profile_not-autoretire:
+ user: not-autoretire
+ account_name:
+ times_url:
+
discord_profile_sotsugyoukigyoshozoku:
user: sotsugyoukigyoshozoku
account_name:
diff --git a/db/fixtures/talks.yml b/db/fixtures/talks.yml
index 15efbd6d02e..50383dd2345 100644
--- a/db/fixtures/talks.yml
+++ b/db/fixtures/talks.yml
@@ -191,3 +191,23 @@ talk_advisernocolleguetrainee:
talk_nagai-kyuukai:
user: nagai-kyuukai
action_completed: true
+
+talk_autoretire-within-1-hour:
+ user: autoretire-within-1-hour
+ action_completed: true
+
+talk_autoretire-within-24-hour:
+ user: autoretire-within-24-hour
+ action_completed: true
+
+talk_autoretire-within-1-week:
+ user: autoretire-within-1-week
+ action_completed: true
+
+talk_autoretire-over-1-week:
+ user: autoretire-over-1-week
+ action_completed: true
+
+talk_not-autoretire:
+ user: not-autoretire
+ action_completed: true
diff --git a/db/fixtures/users.yml b/db/fixtures/users.yml
index 046c987edbb..073a04284fa 100644
--- a/db/fixtures/users.yml
+++ b/db/fixtures/users.yml
@@ -1051,6 +1051,147 @@ kyuukai:
created_at: "2014-01-01 00:00:13"
sent_student_followup_message: true
+autoretire-within-1-hour:
+ login_name: autoretire-within-1-hour
+ email: autoretire-within-1-hour@fjord.jp
+ crypted_password: $2a$10$n/xv4/1luueN6plzm2OyDezWlZFyGHjQEf4hwAW1r3k.lCm0frPK. # testtest
+ salt: zW3kQ9ubsxQQtzzzs4ap
+ name: autoretire-within-1-hour
+ name_kana: 自動退会まで1時間のユーザー
+ customer_id: cus_12345678
+ subscription_id: sub_12345678
+ twitter_account: autoretire-within-1-hour
+ facebook_url: https://www.facebook.com/fjordllc/autoretire-within-1-hour
+ blog_url: https://example.com/autoretire-within-1-hour
+ description: "1時間以内に自動退会するユーザーです。"
+ customer_id: cus_LZ2wCJqYybuDlJ
+ subscription_id: sub_12345678
+ course: course1
+ job: office_worker
+ os: mac
+ experience: inexperienced
+ country_code: JP
+ subdivision_code: '04'
+ github_account: autoretire-within-1-hour
+ unsubscribe_email_token: k3a49_NwgTsiJS0oHGU2Fw
+ hibernated_at: <%= Time.current - 6.months + 30.minutes %>
+ updated_at: "2014-01-01 00:00:13"
+ created_at: "2014-01-01 00:00:13"
+ sent_student_followup_message: true
+
+autoretire-within-24-hour:
+ login_name: autoretire-within-24-hour
+ email: autoretire-within-24-hour@fjord.jp
+ crypted_password: $2a$10$n/xv4/1luueN6plzm2OyDezWlZFyGHjQEf4hwAW1r3k.lCm0frPK. # testtest
+ salt: zW3kQ9ubsxQQtzzzs4ap
+ name: autoretire-within-24-hour
+ name_kana: 自動退会まで24時間のユーザー
+ customer_id: cus_12345678
+ subscription_id: sub_12345678
+ twitter_account: autoretire-within-24-hour
+ facebook_url: https://www.facebook.com/fjordllc/autoretire-within-24-hour
+ blog_url: https://example.com/autoretire-within-24-hour
+ description: "24時間以内に自動退会するユーザーです。"
+ customer_id: cus_LZ2wCJqYybuDlJ
+ subscription_id: sub_12345678
+ course: course1
+ job: office_worker
+ os: mac
+ experience: inexperienced
+ country_code: JP
+ subdivision_code: '04'
+ github_account: autoretire-within-24-hour
+ unsubscribe_email_token: k3a49_NwgTsiJS0oHGU2Fw
+ hibernated_at: <%= Time.current - 6.months + 23.hour%>
+ updated_at: "2014-01-01 00:00:13"
+ created_at: "2014-01-01 00:00:13"
+ sent_student_followup_message: true
+
+autoretire-within-1-week:
+ login_name: autoretire-within-1-week
+ email: autoretire-within-1-week@fjord.jp
+ crypted_password: $2a$10$n/xv4/1luueN6plzm2OyDezWlZFyGHjQEf4hwAW1r3k.lCm0frPK. # testtest
+ salt: zW3kQ9ubsxQQtzzzs4ap
+ name: autoretire-within-1-week
+ name_kana: 自動退会まで1週間のユーザー
+ customer_id: cus_12345678
+ subscription_id: sub_12345678
+ twitter_account: autoretire-within-1-week
+ facebook_url: https://www.facebook.com/fjordllc/autoretire-within-1-week
+ blog_url: https://example.com/autoretire-within-1-week
+ description: "1週間以内に自動退会するユーザーです。"
+ customer_id: cus_LZ2wCJqYybuDlJ
+ subscription_id: sub_12345678
+ course: course1
+ job: office_worker
+ os: mac
+ experience: inexperienced
+ country_code: JP
+ subdivision_code: '04'
+ github_account: autoretire-within-1-week
+ unsubscribe_email_token: k3a49_NwgTsiJS0oHGU2Fw
+ hibernated_at: <%= Time.current - 6.months + 6.days%>
+ updated_at: "2014-01-01 00:00:13"
+ created_at: "2014-01-01 00:00:13"
+ sent_student_followup_message: true
+
+autoretire-over-1-week:
+ login_name: autoretire-over-1-week
+ email: autoretire-over-1-week@fjord.jp
+ crypted_password: $2a$10$n/xv4/1luueN6plzm2OyDezWlZFyGHjQEf4hwAW1r3k.lCm0frPK. # testtest
+ salt: zW3kQ9ubsxQQtzzzs4ap
+ name: autoretire-over-1-week
+ name_kana: 自動退会まで1週間以上のユーザー
+ customer_id: cus_12345678
+ subscription_id: sub_12345678
+ twitter_account: autoretire-over-1-week
+ facebook_url: https://www.facebook.com/fjordllc/autoretire-over-1-week
+ blog_url: https://example.com/autoretire-over-1-week
+ description: "自動退会まで1週間以上のユーザーです。"
+ customer_id: cus_LZ2wCJqYybuDlJ
+ subscription_id: sub_12345678
+ course: course1
+ job: office_worker
+ os: mac
+ experience: inexperienced
+ country_code: JP
+ subdivision_code: '04'
+ github_account: autoretire-over-1-week
+ unsubscribe_email_token: k3a49_NwgTsiJS0oHGU2Fw
+ hibernated_at: <%= Time.current - 6.months + 3.months%>
+ updated_at: "2014-01-01 00:00:13"
+ created_at: "2014-01-01 00:00:13"
+ sent_student_followup_message: true
+
+not-autoretire:
+ login_name: not-autoretire
+ email: not-autoretire@fjord.jp
+ crypted_password: $2a$10$n/xv4/1luueN6plzm2OyDezWlZFyGHjQEf4hwAW1r3k.lCm0frPK. # testtest
+ salt: zW3kQ9ubsxQQtzzzs4ap
+ name: not-autoretire
+ name_kana: 自動退会しないユーザー
+ customer_id: cus_12345678
+ subscription_id: sub_12345678
+ twitter_account: not-autoretire
+ facebook_url: https://www.facebook.com/fjordllc/not-autoretire
+ blog_url: https://example.com/not-autoretire
+ description: "自動退会しないユーザーです。"
+ customer_id: cus_LZ2wCJqYybuDlJ
+ subscription_id: sub_12345678
+ course: course1
+ job: office_worker
+ os: mac
+ experience: inexperienced
+ country_code: JP
+ subdivision_code: '04'
+ github_account: not-autoretire
+ unsubscribe_email_token: k3a49_NwgTsiJS0oHGU2Fw
+ hibernated_at: <%= Time.current - 6.months%>
+ updated_at: "2014-01-01 00:00:13"
+ created_at: "2014-01-01 00:00:13"
+ sent_student_followup_message: true
+ auto_retire: false
+
sotsugyoukigyoshozoku:
login_name: sotsugyoukigyoshozoku
email: sotsugyoukigyoshozoku@example.com
diff --git a/test/active_decorator_test_case.rb b/test/active_decorator_test_case.rb
new file mode 100644
index 00000000000..fb55ed66726
--- /dev/null
+++ b/test/active_decorator_test_case.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ActiveDecoratorTestCase < ActiveSupport::TestCase
+ include ActionView::TestCase::Behavior
+
+ setup do
+ ActiveDecorator::ViewContext.push(controller.view_context)
+ end
+
+ private
+
+ def decorate(instance)
+ ActiveDecorator::Decorator.instance.decorate(instance)
+ end
+end
diff --git a/test/decorators/book_decorator_test.rb b/test/decorators/book_decorator_test.rb
index 17b50b9e408..6f9b1b86160 100644
--- a/test/decorators/book_decorator_test.rb
+++ b/test/decorators/book_decorator_test.rb
@@ -1,11 +1,12 @@
# frozen_string_literal: true
require 'test_helper'
+require 'active_decorator_test_case'
-class BookDecoratorTest < ActiveSupport::TestCase
+class BookDecoratorTest < ActiveDecoratorTestCase
setup do
- @book1 = ActiveDecorator::Decorator.instance.decorate(books(:book1))
- @book2 = ActiveDecorator::Decorator.instance.decorate(books(:book2))
+ @book1 = decorate(books(:book1))
+ @book2 = decorate(books(:book2))
end
test '#must_read_for_any_practices?' do
diff --git a/test/decorators/bookmark_decorator_test.rb b/test/decorators/bookmark_decorator_test.rb
index 268fcf5db5b..2eed0f36cd0 100644
--- a/test/decorators/bookmark_decorator_test.rb
+++ b/test/decorators/bookmark_decorator_test.rb
@@ -1,21 +1,13 @@
# frozen_string_literal: true
require 'test_helper'
+require 'active_decorator_test_case'
-class BookmarkDecoratorTest < ActiveSupport::TestCase
- def setup
- @bookmark31 = ActiveDecorator::Decorator.instance.decorate(bookmarks(:bookmark31))
- @bookmark30 = ActiveDecorator::Decorator.instance.decorate(bookmarks(:bookmark30))
- @bookmark29 = ActiveDecorator::Decorator.instance.decorate(bookmarks(:bookmark29))
- @bookmark28 = ActiveDecorator::Decorator.instance.decorate(bookmarks(:bookmark28))
- @bookmark27 = ActiveDecorator::Decorator.instance.decorate(bookmarks(:bookmark27))
- end
-
+class BookmarkDecoratorTest < ActiveDecoratorTestCase
test '#reported_on_or_created_at' do
- assert_equal I18n.l(bookmarks(:bookmark31).bookmarkable.created_at), I18n.l(@bookmark31.reported_on_or_created_at)
- assert_equal I18n.l(bookmarks(:bookmark30).bookmarkable.created_at), I18n.l(@bookmark30.reported_on_or_created_at)
- assert_equal I18n.l(bookmarks(:bookmark29).bookmarkable.created_at), I18n.l(@bookmark29.reported_on_or_created_at)
- assert_equal I18n.l(bookmarks(:bookmark28).bookmarkable.created_at), I18n.l(@bookmark28.reported_on_or_created_at)
- assert_equal I18n.l(bookmarks(:bookmark27).bookmarkable.reported_on), I18n.l(@bookmark27.reported_on_or_created_at)
+ bookmark31 = decorate(bookmarks(:bookmark31))
+ bookmark27 = decorate(bookmarks(:bookmark27))
+ assert_equal bookmark31.bookmarkable.created_at, bookmark31.reported_on_or_created_at
+ assert_equal bookmark27.bookmarkable.reported_on, bookmark27.reported_on_or_created_at
end
end
diff --git a/test/decorators/company_decorator_test.rb b/test/decorators/company_decorator_test.rb
index e61da7772e7..1b534aa9d86 100644
--- a/test/decorators/company_decorator_test.rb
+++ b/test/decorators/company_decorator_test.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
require 'test_helper'
+require 'active_decorator_test_case'
-class CompanyDecoratorTest < ActiveSupport::TestCase
- def setup
+class CompanyDecoratorTest < ActiveDecoratorTestCase
+ setup do
controller = ApplicationController.new
controller.request = ActionDispatch::TestRequest.create
ActiveDecorator::ViewContext.push controller.view_context
- @company1 = ActiveDecorator::Decorator.instance.decorate(companies(:company1))
+ @company1 = decorate(companies(:company1))
end
test '#adviser_sign_up_url' do
diff --git a/test/decorators/user_decorator/retire_test.rb b/test/decorators/user_decorator/retire_test.rb
new file mode 100644
index 00000000000..94dd37b9d68
--- /dev/null
+++ b/test/decorators/user_decorator/retire_test.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+require 'active_decorator_test_case'
+
+module UserDecorator
+ class RetireTest < ActiveDecoratorTestCase
+ setup do
+ @student = decorate(users(:hajime))
+ @hibernationed = decorate(users(:kyuukai))
+ end
+
+ test '#retire_countdown' do
+ travel_to Time.zone.local(2020, 6, 24, 9, 0, 0) do
+ assert_equal 1.week, @hibernationed.retire_countdown
+ assert_nil @student.retire_countdown
+ end
+ end
+
+ test '#retire_deadline wihin 1 hour' do
+ travel_to Time.zone.local(2020, 7, 1, 8, 1, 0) do
+ assert_equal '2020年07月01日(水) 09:00 (自動退会まであと59分)', @hibernationed.retire_deadline
+ end
+ end
+
+ test '#retire_deadline within 24 hours' do
+ travel_to Time.zone.local(2020, 6, 30, 10, 0, 0) do
+ assert_equal '2020年07月01日(水) 09:00 (自動退会まであと23時間)', @hibernationed.retire_deadline
+ end
+ end
+
+ test '#retire_deadline within 1 week' do
+ travel_to Time.zone.local(2020, 6, 24, 9, 0, 0) do
+ assert_equal '2020年07月01日(水) 09:00 (自動退会まであと7日)', @hibernationed.retire_deadline
+ end
+ end
+
+ test '#retire_deadline over 1 week' do
+ travel_to Time.zone.local(2020, 1, 1, 9, 0, 0) do
+ assert_equal '2020年07月01日(水) 09:00 (自動退会まであと182日)', @hibernationed.retire_deadline
+ end
+ end
+
+ test '#countdown_danger_tag' do
+ travel_to Time.zone.local(2020, 7, 1, 8, 1, 0) do
+ assert_equal 'is-danger', @hibernationed.countdown_danger_tag
+ end
+
+ travel_to Time.zone.local(2020, 1, 1, 9, 0, 0) do
+ assert_equal '', @hibernationed.countdown_danger_tag
+ end
+ end
+ end
+end
diff --git a/test/decorators/user_decorator/role_test.rb b/test/decorators/user_decorator/role_test.rb
new file mode 100644
index 00000000000..29b7b2fce9d
--- /dev/null
+++ b/test/decorators/user_decorator/role_test.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+require 'active_decorator_test_case'
+
+module UserDecorator
+ class RoleTest < ActiveDecoratorTestCase
+ test '#staff_roles' do
+ admin_mentor = decorate(users(:komagata))
+ student = decorate(users(:hajime))
+
+ assert_equal '管理者、メンター', admin_mentor.staff_roles
+ assert_equal '', student.staff_roles
+ end
+
+ test '#roles_to_s' do
+ admin_mentor = decorate(users(:komagata))
+ student = decorate(users(:hajime))
+ graduated = decorate(users(:sotugyou))
+ admin = decorate(users(:adminonly))
+ adviser = decorate(users(:advijirou))
+ mentor = decorate(users(:mentormentaro))
+ trainee = decorate(users(:kensyu))
+ retired = decorate(users(:taikai))
+ hibernationed = decorate(users(:kyuukai))
+
+ assert_equal '管理者、メンター', admin_mentor.roles_to_s
+ assert_equal '', student.roles_to_s
+ assert_equal '卒業生', graduated.roles_to_s
+ assert_equal '管理者', admin.roles_to_s
+ assert_equal 'アドバイザー', adviser.roles_to_s
+ assert_equal 'メンター', mentor.roles_to_s
+ assert_equal '研修生', trainee.roles_to_s
+ assert_equal '退会ユーザー', retired.roles_to_s
+ assert_equal '休会ユーザー', hibernationed.roles_to_s
+ end
+ end
+end
diff --git a/test/decorators/user_decorator_test.rb b/test/decorators/user_decorator_test.rb
index 4eb514442b5..a4d23e7cbcc 100644
--- a/test/decorators/user_decorator_test.rb
+++ b/test/decorators/user_decorator_test.rb
@@ -1,29 +1,18 @@
# frozen_string_literal: true
require 'test_helper'
+require 'active_decorator_test_case'
-class UserDecoratorTest < ActiveSupport::TestCase
- include ActionView::TestCase::Behavior
-
- def setup
- ActiveDecorator::ViewContext.push(controller.view_context)
- @admin_mentor_user = ActiveDecorator::Decorator.instance.decorate(users(:komagata))
- @student_user = ActiveDecorator::Decorator.instance.decorate(users(:hajime))
- @graduated_user = ActiveDecorator::Decorator.instance.decorate(users(:sotugyou))
- @admin_user = ActiveDecorator::Decorator.instance.decorate(users(:adminonly))
- @adviser_user = ActiveDecorator::Decorator.instance.decorate(users(:advijirou))
- @mentor_user = ActiveDecorator::Decorator.instance.decorate(users(:mentormentaro))
- @trainee_user = ActiveDecorator::Decorator.instance.decorate(users(:kensyu))
- @retired_user = ActiveDecorator::Decorator.instance.decorate(users(:taikai))
- @hibernationed_user = ActiveDecorator::Decorator.instance.decorate(users(:kyuukai))
- @japanese_user = ActiveDecorator::Decorator.instance.decorate(users(:kimura))
- @american_user = ActiveDecorator::Decorator.instance.decorate(users(:tom))
- @subdivision_not_registered_user = ActiveDecorator::Decorator.instance.decorate(users(:hatsuno))
- end
-
- test '#staff_roles' do
- assert_equal '管理者、メンター', @admin_mentor_user.staff_roles
- assert_equal '', @student_user.staff_roles
+class UserDecoratorTest < ActiveDecoratorTestCase
+ setup do
+ @admin_mentor_user = decorate(users(:komagata))
+ @student_user = decorate(users(:hajime))
+ @graduated_user = decorate(users(:sotugyou))
+ @mentor_user = decorate(users(:mentormentaro))
+ @hibernationed_user = decorate(users(:kyuukai))
+ @japanese_user = decorate(users(:kimura))
+ @american_user = decorate(users(:tom))
+ @subdivision_not_registered_user = decorate(users(:hatsuno))
end
test '#icon_title' do
@@ -44,18 +33,6 @@ def setup
@graduated_user.enrollment_period
end
- test '#roles_to_s' do
- assert_equal '管理者、メンター', @admin_mentor_user.roles_to_s
- assert_equal '', @student_user.roles_to_s
- assert_equal '卒業生', @graduated_user.roles_to_s
- assert_equal '管理者', @admin_user.roles_to_s
- assert_equal 'アドバイザー', @adviser_user.roles_to_s
- assert_equal 'メンター', @mentor_user.roles_to_s
- assert_equal '研修生', @trainee_user.roles_to_s
- assert_equal '退会ユーザー', @retired_user.roles_to_s
- assert_equal '休会ユーザー', @hibernationed_user.roles_to_s
- end
-
test '#subdivisions_of_country' do
assert_includes @japanese_user.subdivisions_of_country, %w[北海道 01]
assert_includes @american_user.subdivisions_of_country, %w[アラスカ州 AK]
@@ -66,4 +43,11 @@ def setup
assert_equal 'ニューヨーク州 (米国)', @american_user.address
assert_equal '日本', @subdivision_not_registered_user.address
end
+
+ test '#hibernation_days' do
+ travel_to Time.zone.local(2020, 2, 1, 9, 0, 0) do
+ assert_equal 31, @hibernationed_user.hibernation_days
+ assert_nil @student_user.hibernation_days
+ end
+ end
end
diff --git a/test/integration/api/products/unassigned_counts_test.rb b/test/integration/api/products/unassigned_counts_test.rb
index 03d52d6e2ae..d463298ec8c 100644
--- a/test/integration/api/products/unassigned_counts_test.rb
+++ b/test/integration/api/products/unassigned_counts_test.rb
@@ -6,7 +6,7 @@ class API::Products::UnassignedTextTest < ActionDispatch::IntegrationTest
fixtures :products
test 'GET /api/products/unassigned/counts.txt' do
- products(:product15).update_column(:checker_id, nil) # rubocop:disable Rails/SkipsModelValidations
+ products(:product15).update_column(:checker_id, nil) # rubocop:disable Rails/SkipsModelValidations
get counts_api_products_unassigned_index_path(format: :text)
assert_response :unauthorized
diff --git a/test/models/user_test.rb b/test/models/user_test.rb
index e3d9eccb3ef..59f4e766029 100644
--- a/test/models/user_test.rb
+++ b/test/models/user_test.rb
@@ -703,4 +703,9 @@ class UserTest < ActiveSupport::TestCase
user.delete_and_assign_new_organizer
end
end
+
+ test '#scheduled_retire_at' do
+ assert_equal '2020-07-01 09:00:00 +0900', users(:kyuukai).scheduled_retire_at.to_s
+ assert_nil users(:hatsuno).scheduled_retire_at
+ end
end
diff --git a/test/system/articles_test.rb b/test/system/articles_test.rb
index 85c13a73f98..2c6428f32b0 100644
--- a/test/system/articles_test.rb
+++ b/test/system/articles_test.rb
@@ -134,7 +134,7 @@ class ArticlesTest < ApplicationSystemTestCase
end
visit_with_auth articles_url, 'komagata'
- find 'nav.pagination'
+ assert_selector 'nav.pagination', count: 2
end
test "general user can't see edit and delete buttons" do
diff --git a/test/system/product/self_assigned_test.rb b/test/system/product/self_assigned_test.rb
index c15581a1a16..fac94ec2387 100644
--- a/test/system/product/self_assigned_test.rb
+++ b/test/system/product/self_assigned_test.rb
@@ -147,4 +147,65 @@ class Product::SelfAssignedTest < ApplicationSystemTestCase
visit_with_auth '/products/self_assigned?target=self_assigned_no_replied', 'mentormentaro'
assert_text '未返信の担当提出物はありません'
end
+
+ test "the number of products displayed in a self assigned tab excludes hibernated users' products" do
+ checker = users(:mentormentaro)
+
+ # 担当中かつ未返信の提出物が0個のとき、「自分の担当」タブの右肩の赤い数字がそもそも表示されないという状況を防ぐため、この時点で提出物を1件作成
+ Product.create!(
+ body: 'test',
+ user: users(:kimura),
+ practice: practices(:practice5),
+ checker_id: checker.id
+ )
+
+ self_assigned_count = Product.unhibernated_user_products.self_assigned_product(checker.id).unchecked.count
+ self_assigned_no_replied_count = Product.unhibernated_user_products.self_assigned_no_replied_products(checker.id).unchecked.count
+
+ visit_with_auth '/products/self_assigned', 'mentormentaro'
+ assert_selector '.page-tabs__item-link.is-active', text: "自分の担当 (#{self_assigned_count})"
+ assert_selector '.page-tabs__item-count.a-notification-count.is-only-mentor', text: self_assigned_no_replied_count.to_s
+
+ Product.create!(
+ body: 'test',
+ user: users(:kyuukai),
+ practice: practices(:practice5),
+ checker_id: checker.id
+ )
+
+ visit_with_auth '/products/self_assigned', 'mentormentaro'
+ assert_selector '.page-tabs__item-link.is-active', text: "自分の担当 (#{self_assigned_count})"
+ assert_selector '.page-tabs__item-count.a-notification-count.is-only-mentor', text: self_assigned_no_replied_count.to_s
+ end
+
+ test "not display hibernated users' products in a self assigned products list" do
+ checker = users(:mentormentaro)
+ unhibernated_user = users(:kimura)
+ hibernated_user = users(:kyuukai)
+ practice = practices(:practice5)
+
+ Product.create!(
+ body: 'test',
+ user: unhibernated_user,
+ practice: practice,
+ checker_id: checker.id
+ )
+ Product.create!(
+ body: 'test',
+ user: hibernated_user,
+ practice: practice,
+ checker_id: checker.id
+ )
+
+ unhibernated_users_displayed_name = "#{unhibernated_user.login_name} (#{unhibernated_user.name_kana})"
+ hibernated_users_displayed_name = "#{hibernated_user.login_name} (#{hibernated_user.name_kana})"
+
+ visit_with_auth '/products/self_assigned?target=self_assigned_all', 'mentormentaro'
+ assert_text unhibernated_users_displayed_name
+ assert_no_text hibernated_users_displayed_name
+
+ visit_with_auth '/products/self_assigned?target=self_assigned_no_replied', 'mentormentaro'
+ assert_text unhibernated_users_displayed_name
+ assert_no_text hibernated_users_displayed_name
+ end
end