Implement UI for Admin Search of Hashtags (#30880)
This commit is contained in:
parent
6d2ed0dcba
commit
c40e481169
17 changed files with 316 additions and 11 deletions
|
@ -2,7 +2,15 @@
|
||||||
|
|
||||||
module Admin
|
module Admin
|
||||||
class TagsController < BaseController
|
class TagsController < BaseController
|
||||||
before_action :set_tag
|
before_action :set_tag, except: [:index]
|
||||||
|
|
||||||
|
PER_PAGE = 20
|
||||||
|
|
||||||
|
def index
|
||||||
|
authorize :tag, :index?
|
||||||
|
|
||||||
|
@tags = filtered_tags.page(params[:page]).per(PER_PAGE)
|
||||||
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
authorize @tag, :show?
|
authorize @tag, :show?
|
||||||
|
@ -31,5 +39,13 @@ module Admin
|
||||||
def tag_params
|
def tag_params
|
||||||
params.require(:tag).permit(:name, :display_name, :trendable, :usable, :listable)
|
params.require(:tag).permit(:name, :display_name, :trendable, :usable, :listable)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filtered_tags
|
||||||
|
TagFilter.new(filter_params.with_defaults(order: 'newest')).results
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_params
|
||||||
|
params.slice(:page, *TagFilter::KEYS).permit(:page, *TagFilter::KEYS)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
15
app/helpers/admin/tags_helper.rb
Normal file
15
app/helpers/admin/tags_helper.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Admin::TagsHelper
|
||||||
|
def admin_tags_moderation_options
|
||||||
|
[
|
||||||
|
[t('admin.tags.moderation.reviewed'), 'reviewed'],
|
||||||
|
[t('admin.tags.moderation.review_requested'), 'review_requested'],
|
||||||
|
[t('admin.tags.moderation.unreviewed'), 'unreviewed'],
|
||||||
|
[t('admin.tags.moderation.trendable'), 'trendable'],
|
||||||
|
[t('admin.tags.moderation.not_trendable'), 'not_trendable'],
|
||||||
|
[t('admin.tags.moderation.usable'), 'usable'],
|
||||||
|
[t('admin.tags.moderation.not_usable'), 'not_usable'],
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
|
@ -350,6 +350,10 @@
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning-hint {
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__body {
|
&__body {
|
||||||
|
|
|
@ -286,6 +286,10 @@ a.table-action-link {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--padded {
|
||||||
|
padding: 12px 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
&--with-image {
|
&--with-image {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
74
app/models/admin/tag_filter.rb
Normal file
74
app/models/admin/tag_filter.rb
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Admin::TagFilter
|
||||||
|
KEYS = %i(
|
||||||
|
status
|
||||||
|
name
|
||||||
|
order
|
||||||
|
).freeze
|
||||||
|
|
||||||
|
attr_reader :params
|
||||||
|
|
||||||
|
def initialize(params)
|
||||||
|
@params = params.to_h.symbolize_keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def results
|
||||||
|
scope = Tag.reorder(nil)
|
||||||
|
|
||||||
|
params.each do |key, value|
|
||||||
|
next if key == :page
|
||||||
|
|
||||||
|
scope.merge!(scope_for(key, value)) if value.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
scope
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def scope_for(key, value)
|
||||||
|
case key
|
||||||
|
when :status
|
||||||
|
status_scope(value)
|
||||||
|
when :name
|
||||||
|
Tag.search_for(value.to_s.strip, params[:limit], params[:offset], exclude_unlistable: false)
|
||||||
|
when :order
|
||||||
|
order_scope(value)
|
||||||
|
else
|
||||||
|
raise Mastodon::InvalidParameterError, "Unknown filter: #{key}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def status_scope(value)
|
||||||
|
case value.to_s
|
||||||
|
when 'reviewed'
|
||||||
|
Tag.reviewed
|
||||||
|
when 'review_requested'
|
||||||
|
Tag.pending_review
|
||||||
|
when 'unreviewed'
|
||||||
|
Tag.unreviewed
|
||||||
|
when 'trendable'
|
||||||
|
Tag.trendable
|
||||||
|
when 'not_trendable'
|
||||||
|
Tag.not_trendable
|
||||||
|
when 'usable'
|
||||||
|
Tag.usable
|
||||||
|
when 'not_usable'
|
||||||
|
Tag.not_usable
|
||||||
|
else
|
||||||
|
raise Mastodon::InvalidParameterError, "Unknown status: #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def order_scope(value)
|
||||||
|
case value.to_s
|
||||||
|
when 'newest'
|
||||||
|
Tag.order(created_at: :desc)
|
||||||
|
when 'oldest'
|
||||||
|
Tag.order(created_at: :asc)
|
||||||
|
else
|
||||||
|
raise Mastodon::InvalidParameterError, "Unknown order: #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -52,6 +52,7 @@ class Tag < ApplicationRecord
|
||||||
scope :unreviewed, -> { where(reviewed_at: nil) }
|
scope :unreviewed, -> { where(reviewed_at: nil) }
|
||||||
scope :pending_review, -> { unreviewed.where.not(requested_review_at: nil) }
|
scope :pending_review, -> { unreviewed.where.not(requested_review_at: nil) }
|
||||||
scope :usable, -> { where(usable: [true, nil]) }
|
scope :usable, -> { where(usable: [true, nil]) }
|
||||||
|
scope :not_usable, -> { where(usable: false) }
|
||||||
scope :listable, -> { where(listable: [true, nil]) }
|
scope :listable, -> { where(listable: [true, nil]) }
|
||||||
scope :trendable, -> { Setting.trendable_by_default ? where(trendable: [true, nil]) : where(trendable: true) }
|
scope :trendable, -> { Setting.trendable_by_default ? where(trendable: [true, nil]) : where(trendable: true) }
|
||||||
scope :not_trendable, -> { where(trendable: false) }
|
scope :not_trendable, -> { where(trendable: false) }
|
||||||
|
@ -74,6 +75,10 @@ class Tag < ApplicationRecord
|
||||||
attributes['display_name'] || name
|
attributes['display_name'] || name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def formatted_name
|
||||||
|
"##{display_name}"
|
||||||
|
end
|
||||||
|
|
||||||
def usable
|
def usable
|
||||||
boolean_with_default('usable', true)
|
boolean_with_default('usable', true)
|
||||||
end
|
end
|
||||||
|
@ -132,8 +137,10 @@ class Tag < ApplicationRecord
|
||||||
|
|
||||||
def search_for(term, limit = 5, offset = 0, options = {})
|
def search_for(term, limit = 5, offset = 0, options = {})
|
||||||
stripped_term = term.strip
|
stripped_term = term.strip
|
||||||
|
options.reverse_merge!({ exclude_unlistable: true, exclude_unreviewed: false })
|
||||||
|
|
||||||
query = Tag.listable.matches_name(stripped_term)
|
query = Tag.matches_name(stripped_term)
|
||||||
|
query = query.merge(Tag.listable) if options[:exclude_unlistable]
|
||||||
query = query.merge(matching_name(stripped_term).or(where.not(reviewed_at: nil))) if options[:exclude_unreviewed]
|
query = query.merge(matching_name(stripped_term).or(where.not(reviewed_at: nil))) if options[:exclude_unreviewed]
|
||||||
|
|
||||||
query.order(Arel.sql('length(name) ASC, name ASC'))
|
query.order(Arel.sql('length(name) ASC, name ASC'))
|
||||||
|
|
27
app/views/admin/tags/_tag.html.haml
Normal file
27
app/views/admin/tags/_tag.html.haml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
.batch-table__row{ class: [!tag.requires_review? && !tag.usable? && 'batch-table__row--muted'] }
|
||||||
|
.batch-table__row__content.batch-table__row__content--padded.pending-account
|
||||||
|
.pending-account__header
|
||||||
|
%strong
|
||||||
|
= link_to tag.formatted_name, admin_tag_path(tag.id)
|
||||||
|
|
||||||
|
%br/
|
||||||
|
|
||||||
|
- if tag.usable?
|
||||||
|
= t('admin.tags.moderation.usable')
|
||||||
|
- else
|
||||||
|
= t('admin.tags.moderation.not_usable')
|
||||||
|
|
||||||
|
·
|
||||||
|
- if tag.trendable?
|
||||||
|
= t('admin.tags.moderation.trendable')
|
||||||
|
- else
|
||||||
|
= t('admin.tags.moderation.not_trendable')
|
||||||
|
|
||||||
|
- if tag.requested_review? || tag.requires_review?
|
||||||
|
·
|
||||||
|
- if tag.requested_review?
|
||||||
|
%span.negative-hint
|
||||||
|
= t('admin.tags.moderation.review_requested')
|
||||||
|
- else
|
||||||
|
%span.warning-hint
|
||||||
|
= t('admin.tags.moderation.pending_review')
|
39
app/views/admin/tags/index.html.haml
Normal file
39
app/views/admin/tags/index.html.haml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
- content_for :page_title do
|
||||||
|
= t('admin.tags.title')
|
||||||
|
|
||||||
|
= form_with url: admin_tags_url, method: :get, class: :simple_form do |form|
|
||||||
|
.filters
|
||||||
|
.filter-subset.filter-subset--with-select
|
||||||
|
%strong= t('admin.tags.moderation.title')
|
||||||
|
.input.select.optional
|
||||||
|
= form.select :status,
|
||||||
|
options_for_select(admin_tags_moderation_options, params[:status]),
|
||||||
|
prompt: t('generic.all')
|
||||||
|
|
||||||
|
.filter-subset.filter-subset--with-select
|
||||||
|
%strong= t 'generic.order_by'
|
||||||
|
.input.select
|
||||||
|
= form.select :order,
|
||||||
|
options_for_select([[t('admin.tags.newest'), 'newest'], [t('admin.tags.oldest'), 'oldest']], params[:order])
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
.input.string.optional
|
||||||
|
= form.text_field :name,
|
||||||
|
value: params[:name],
|
||||||
|
class: 'string optional',
|
||||||
|
placeholder: t('admin.tags.name')
|
||||||
|
|
||||||
|
.actions
|
||||||
|
%button.button= t('admin.tags.search')
|
||||||
|
= link_to t('admin.tags.reset'), admin_tags_path, class: 'button negative'
|
||||||
|
|
||||||
|
%hr.spacer/
|
||||||
|
|
||||||
|
.batch-table
|
||||||
|
.batch-table__body
|
||||||
|
- if @tags.empty?
|
||||||
|
= nothing_here 'nothing-here--under-tabs'
|
||||||
|
- else
|
||||||
|
= render partial: 'tag', collection: @tags
|
||||||
|
|
||||||
|
= paginate @tags
|
|
@ -1,12 +1,13 @@
|
||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
= "##{@tag.display_name}"
|
= @tag.formatted_name
|
||||||
|
|
||||||
- if current_user.can?(:view_dashboard)
|
- content_for :heading_actions do
|
||||||
- content_for :heading_actions do
|
- if current_user.can?(:view_dashboard)
|
||||||
= l(@time_period.first)
|
= l(@time_period.first)
|
||||||
= ' - '
|
= ' - '
|
||||||
= l(@time_period.last)
|
= l(@time_period.last)
|
||||||
|
|
||||||
|
- if current_user.can?(:view_dashboard)
|
||||||
.dashboard
|
.dashboard
|
||||||
.dashboard__item
|
.dashboard__item
|
||||||
= react_admin_component :counter,
|
= react_admin_component :counter,
|
||||||
|
|
|
@ -4,9 +4,7 @@
|
||||||
|
|
||||||
.batch-table__row__content.pending-account
|
.batch-table__row__content.pending-account
|
||||||
.pending-account__header
|
.pending-account__header
|
||||||
= link_to admin_tag_path(tag.id) do
|
= link_to tag.formatted_name, admin_tag_path(tag.id)
|
||||||
= material_symbol 'tag'
|
|
||||||
= tag.display_name
|
|
||||||
|
|
||||||
%br/
|
%br/
|
||||||
|
|
||||||
|
|
|
@ -885,7 +885,23 @@ en:
|
||||||
action: Check here for more information
|
action: Check here for more information
|
||||||
message_html: "<strong>Your object storage is misconfigured. The privacy of your users is at risk.</strong>"
|
message_html: "<strong>Your object storage is misconfigured. The privacy of your users is at risk.</strong>"
|
||||||
tags:
|
tags:
|
||||||
|
moderation:
|
||||||
|
not_trendable: Not trendable
|
||||||
|
not_usable: Not usable
|
||||||
|
pending_review: Pending review
|
||||||
|
review_requested: Review requested
|
||||||
|
reviewed: Reviewed
|
||||||
|
title: Status
|
||||||
|
trendable: Trendable
|
||||||
|
unreviewed: Unreviewed
|
||||||
|
usable: Usable
|
||||||
|
name: Name
|
||||||
|
newest: Newest
|
||||||
|
oldest: Oldest
|
||||||
|
reset: Reset
|
||||||
review: Review status
|
review: Review status
|
||||||
|
search: Search
|
||||||
|
title: Hashtags
|
||||||
updated_msg: Hashtag settings updated successfully
|
updated_msg: Hashtag settings updated successfully
|
||||||
title: Administration
|
title: Administration
|
||||||
trends:
|
trends:
|
||||||
|
|
|
@ -314,7 +314,7 @@ en:
|
||||||
listable: Allow this hashtag to appear in searches and suggestions
|
listable: Allow this hashtag to appear in searches and suggestions
|
||||||
name: Hashtag
|
name: Hashtag
|
||||||
trendable: Allow this hashtag to appear under trends
|
trendable: Allow this hashtag to appear under trends
|
||||||
usable: Allow posts to use this hashtag
|
usable: Allow posts to use this hashtag locally
|
||||||
user:
|
user:
|
||||||
role: Role
|
role: Role
|
||||||
time_zone: Time zone
|
time_zone: Time zone
|
||||||
|
|
|
@ -40,13 +40,14 @@ SimpleNavigation::Configuration.run do |navigation|
|
||||||
|
|
||||||
n.item :trends, safe_join([fa_icon('fire fw'), t('admin.trends.title')]), admin_trends_statuses_path, if: -> { current_user.can?(:manage_taxonomies) && !self_destruct } do |s|
|
n.item :trends, safe_join([fa_icon('fire fw'), t('admin.trends.title')]), admin_trends_statuses_path, if: -> { current_user.can?(:manage_taxonomies) && !self_destruct } do |s|
|
||||||
s.item :statuses, safe_join([fa_icon('comments-o fw'), t('admin.trends.statuses.title')]), admin_trends_statuses_path, highlights_on: %r{/admin/trends/statuses}
|
s.item :statuses, safe_join([fa_icon('comments-o fw'), t('admin.trends.statuses.title')]), admin_trends_statuses_path, highlights_on: %r{/admin/trends/statuses}
|
||||||
s.item :tags, safe_join([fa_icon('hashtag fw'), t('admin.trends.tags.title')]), admin_trends_tags_path, highlights_on: %r{/admin/tags|/admin/trends/tags}
|
s.item :tags, safe_join([fa_icon('hashtag fw'), t('admin.trends.tags.title')]), admin_trends_tags_path, highlights_on: %r{/admin/trends/tags}
|
||||||
s.item :links, safe_join([fa_icon('newspaper-o fw'), t('admin.trends.links.title')]), admin_trends_links_path, highlights_on: %r{/admin/trends/links}
|
s.item :links, safe_join([fa_icon('newspaper-o fw'), t('admin.trends.links.title')]), admin_trends_links_path, highlights_on: %r{/admin/trends/links}
|
||||||
end
|
end
|
||||||
|
|
||||||
n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), nil, if: -> { current_user.can?(:manage_reports, :view_audit_log, :manage_users, :manage_invites, :manage_taxonomies, :manage_federation, :manage_blocks) && !self_destruct } do |s|
|
n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), nil, if: -> { current_user.can?(:manage_reports, :view_audit_log, :manage_users, :manage_invites, :manage_taxonomies, :manage_federation, :manage_blocks) && !self_destruct } do |s|
|
||||||
s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_path, highlights_on: %r{/admin/reports|admin/report_notes}, if: -> { current_user.can?(:manage_reports) }
|
s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_path, highlights_on: %r{/admin/reports|admin/report_notes}, if: -> { current_user.can?(:manage_reports) }
|
||||||
s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_path(origin: 'local'), highlights_on: %r{/admin/accounts|admin/account_moderation_notes|/admin/pending_accounts|/admin/disputes|/admin/users}, if: -> { current_user.can?(:manage_users) }
|
s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_path(origin: 'local'), highlights_on: %r{/admin/accounts|admin/account_moderation_notes|/admin/pending_accounts|/admin/disputes|/admin/users}, if: -> { current_user.can?(:manage_users) }
|
||||||
|
s.item :tags, safe_join([fa_icon('hashtag fw'), t('admin.tags.title')]), admin_tags_path, highlights_on: %r{/admin/tags}, if: -> { current_user.can?(:manage_taxonomies) }
|
||||||
s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path, if: -> { current_user.can?(:manage_invites) }
|
s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path, if: -> { current_user.can?(:manage_invites) }
|
||||||
s.item :follow_recommendations, safe_join([fa_icon('user-plus fw'), t('admin.follow_recommendations.title')]), admin_follow_recommendations_path, highlights_on: %r{/admin/follow_recommendations}, if: -> { current_user.can?(:manage_taxonomies) }
|
s.item :follow_recommendations, safe_join([fa_icon('user-plus fw'), t('admin.follow_recommendations.title')]), admin_follow_recommendations_path, highlights_on: %r{/admin/follow_recommendations}, if: -> { current_user.can?(:manage_taxonomies) }
|
||||||
s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_path(limited: limited_federation_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.can?(:manage_federation) }
|
s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_path(limited: limited_federation_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.can?(:manage_federation) }
|
||||||
|
|
|
@ -163,7 +163,7 @@ namespace :admin do
|
||||||
resources :roles, except: [:show]
|
resources :roles, except: [:show]
|
||||||
resources :account_moderation_notes, only: [:create, :destroy]
|
resources :account_moderation_notes, only: [:create, :destroy]
|
||||||
resource :follow_recommendations, only: [:show, :update]
|
resource :follow_recommendations, only: [:show, :update]
|
||||||
resources :tags, only: [:show, :update]
|
resources :tags, only: [:index, :show, :update]
|
||||||
|
|
||||||
namespace :trends do
|
namespace :trends do
|
||||||
resources :links, only: [:index] do
|
resources :links, only: [:index] do
|
||||||
|
|
|
@ -9,6 +9,43 @@ RSpec.describe Admin::TagsController do
|
||||||
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
|
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
before do
|
||||||
|
Fabricate(:tag)
|
||||||
|
|
||||||
|
tag_filter = instance_double(Admin::TagFilter, results: Tag.all)
|
||||||
|
allow(Admin::TagFilter).to receive(:new).and_return(tag_filter)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:params) { { order: 'newest' } }
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(response).to render_template(:index)
|
||||||
|
|
||||||
|
expect(Admin::TagFilter)
|
||||||
|
.to have_received(:new)
|
||||||
|
.with(hash_including(params))
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'with filters' do
|
||||||
|
let(:params) { { order: 'newest', name: 'test' } }
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
get :index, params: { name: 'test' }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
expect(response).to render_template(:index)
|
||||||
|
|
||||||
|
expect(Admin::TagFilter)
|
||||||
|
.to have_received(:new)
|
||||||
|
.with(hash_including(params))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'GET #show' do
|
describe 'GET #show' do
|
||||||
let!(:tag) { Fabricate(:tag) }
|
let!(:tag) { Fabricate(:tag) }
|
||||||
|
|
||||||
|
|
36
spec/models/admin/tag_filter_spec.rb
Normal file
36
spec/models/admin/tag_filter_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Admin::TagFilter do
|
||||||
|
describe 'with invalid params' do
|
||||||
|
it 'raises with key error' do
|
||||||
|
filter = described_class.new(wrong: true)
|
||||||
|
|
||||||
|
expect { filter.results }.to raise_error(/wrong/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises with status scope error' do
|
||||||
|
filter = described_class.new(status: 'unknown')
|
||||||
|
|
||||||
|
expect { filter.results }.to raise_error(/Unknown status: unknown/)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises with order value error' do
|
||||||
|
filter = described_class.new(order: 'unknown')
|
||||||
|
|
||||||
|
expect { filter.results }.to raise_error(/Unknown order: unknown/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#results' do
|
||||||
|
let(:listable_tag) { Fabricate(:tag, name: 'test1', listable: true) }
|
||||||
|
let(:not_listable_tag) { Fabricate(:tag, name: 'test2', listable: false) }
|
||||||
|
|
||||||
|
it 'returns tags filtered by name' do
|
||||||
|
filter = described_class.new(name: 'test')
|
||||||
|
|
||||||
|
expect(filter.results).to eq([listable_tag, not_listable_tag])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -112,6 +112,18 @@ RSpec.describe Tag do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#formatted_name' do
|
||||||
|
it 'returns name with a proceeding hash symbol' do
|
||||||
|
tag = Fabricate(:tag, name: 'foo')
|
||||||
|
expect(tag.formatted_name).to eq '#foo'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns display_name with a proceeding hash symbol, if display name present' do
|
||||||
|
tag = Fabricate(:tag, name: 'foobar', display_name: 'FooBar')
|
||||||
|
expect(tag.formatted_name).to eq '#FooBar'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '.recently_used' do
|
describe '.recently_used' do
|
||||||
let(:account) { Fabricate(:account) }
|
let(:account) { Fabricate(:account) }
|
||||||
let(:other_person_status) { Fabricate(:status) }
|
let(:other_person_status) { Fabricate(:status) }
|
||||||
|
@ -240,5 +252,23 @@ RSpec.describe Tag do
|
||||||
|
|
||||||
expect(results).to eq [tag, similar_tag]
|
expect(results).to eq [tag, similar_tag]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'finds only listable tags' do
|
||||||
|
tag = Fabricate(:tag, name: 'match')
|
||||||
|
_miss_tag = Fabricate(:tag, name: 'matchunlisted', listable: false)
|
||||||
|
|
||||||
|
results = described_class.search_for('match')
|
||||||
|
|
||||||
|
expect(results).to eq [tag]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'finds non-listable tags as well via option' do
|
||||||
|
tag = Fabricate(:tag, name: 'match')
|
||||||
|
unlisted_tag = Fabricate(:tag, name: 'matchunlisted', listable: false)
|
||||||
|
|
||||||
|
results = described_class.search_for('match', 5, 0, exclude_unlistable: false)
|
||||||
|
|
||||||
|
expect(results).to eq [tag, unlisted_tag]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue