2019-04-30 08:27:42 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2022-07-28 10:27:38 +08:00
|
|
|
RSpec.describe CategoriesController do
|
2018-06-04 12:04:32 +08:00
|
|
|
let(:admin) { Fabricate(:admin) }
|
|
|
|
let!(:category) { Fabricate(:category, user: admin) }
|
2020-05-15 10:41:40 +08:00
|
|
|
fab!(:user) { Fabricate(:user) }
|
2018-03-27 14:23:35 +08:00
|
|
|
|
2018-04-24 07:54:51 +08:00
|
|
|
context 'index' do
|
|
|
|
|
2018-07-24 02:54:32 +08:00
|
|
|
it 'web crawler view has correct urls for subfolder install' do
|
2019-11-15 13:48:24 +08:00
|
|
|
set_subfolder "/forum"
|
2018-07-24 02:54:32 +08:00
|
|
|
get '/categories', headers: { 'HTTP_USER_AGENT' => 'Googlebot' }
|
2020-05-05 11:46:57 +08:00
|
|
|
html = Nokogiri::HTML5(response.body)
|
2018-07-24 02:54:32 +08:00
|
|
|
expect(html.css('body.crawler')).to be_present
|
2020-06-18 16:32:14 +08:00
|
|
|
expect(html.css("a[href=\"/forum/c/#{category.slug}/#{category.id}\"]")).to be_present
|
2018-07-24 02:54:32 +08:00
|
|
|
end
|
2018-08-15 07:22:03 +08:00
|
|
|
|
|
|
|
it "properly preloads topic list" do
|
|
|
|
SiteSetting.categories_topics = 5
|
|
|
|
SiteSetting.categories_topics.times { Fabricate(:topic) }
|
|
|
|
get "/categories"
|
2018-09-17 16:31:46 +08:00
|
|
|
|
2018-09-28 17:28:33 +08:00
|
|
|
expect(response.body).to have_tag("div#data-preloaded") do |element|
|
2018-09-17 16:31:46 +08:00
|
|
|
json = JSON.parse(element.current_scope.attribute('data-preloaded').value)
|
|
|
|
expect(json['topic_list_latest']).to include(%{"more_topics_url":"/latest"})
|
|
|
|
end
|
2018-08-15 07:22:03 +08:00
|
|
|
end
|
2018-12-12 18:46:14 +08:00
|
|
|
|
|
|
|
it "Shows correct title if category list is set for homepage" do
|
|
|
|
SiteSetting.top_menu = "categories|latest"
|
|
|
|
get "/"
|
|
|
|
|
|
|
|
expect(response.body).to have_tag "title", text: "Discourse"
|
|
|
|
|
|
|
|
SiteSetting.short_site_description = "Official community"
|
|
|
|
get "/"
|
|
|
|
|
|
|
|
expect(response.body).to have_tag "title", text: "Discourse - Official community"
|
|
|
|
end
|
2019-03-18 22:24:46 +08:00
|
|
|
|
|
|
|
it "redirects /category paths to /c paths" do
|
|
|
|
get "/category/uncategorized"
|
|
|
|
expect(response.status).to eq(302)
|
|
|
|
expect(response.body).to include("c/uncategorized")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "respects permalinks before redirecting /category paths to /c paths" do
|
2020-03-24 13:59:42 +08:00
|
|
|
_perm = Permalink.create!(url: "category/something", category_id: category.id)
|
2019-03-18 22:24:46 +08:00
|
|
|
|
|
|
|
get "/category/something"
|
|
|
|
expect(response.status).to eq(301)
|
|
|
|
expect(response.body).to include(category.slug)
|
|
|
|
end
|
2020-05-15 10:41:40 +08:00
|
|
|
|
|
|
|
it 'returns the right response for a normal user' do
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
get "/categories.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
category_list = response.parsed_body["category_list"]
|
|
|
|
|
|
|
|
expect(category_list["categories"].map { |c| c["id"] }).to contain_exactly(
|
|
|
|
SiteSetting.get(:uncategorized_category_id), category.id
|
|
|
|
)
|
|
|
|
end
|
2021-05-04 11:05:08 +08:00
|
|
|
|
2021-10-06 02:12:31 +08:00
|
|
|
it 'does not returns subcatgories without permission' do
|
|
|
|
subcategory = Fabricate(:category, user: admin, parent_category: category)
|
|
|
|
subcategory.set_permissions(admins: :full)
|
|
|
|
subcategory.save!
|
|
|
|
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
get "/categories.json?include_subcategories=true"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
category_list = response.parsed_body["category_list"]
|
|
|
|
|
|
|
|
subcategories_for_category = category_list["categories"][1]["subcategory_list"]
|
|
|
|
expect(subcategories_for_category).to eq(nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns the right subcategory response with permission' do
|
|
|
|
subcategory = Fabricate(:category, user: admin, parent_category: category)
|
|
|
|
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
get "/categories.json?include_subcategories=true"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
category_list = response.parsed_body["category_list"]
|
|
|
|
|
|
|
|
subcategories_for_category = category_list["categories"][1]["subcategory_list"]
|
|
|
|
expect(subcategories_for_category.count).to eq(1)
|
|
|
|
expect(subcategories_for_category.first["parent_category_id"]).to eq(category.id)
|
|
|
|
expect(subcategories_for_category.first["id"]).to eq(subcategory.id)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not return subcategories without query param' do
|
|
|
|
subcategory = Fabricate(:category, user: admin, parent_category: category)
|
|
|
|
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
get "/categories.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
category_list = response.parsed_body["category_list"]
|
|
|
|
|
|
|
|
subcategories_for_category = category_list["categories"][1]["subcategory_list"]
|
|
|
|
expect(subcategories_for_category).to eq(nil)
|
|
|
|
end
|
|
|
|
|
2022-03-05 05:11:59 +08:00
|
|
|
it 'includes topics for categories, subcategories and subsubcategories when requested' do
|
|
|
|
SiteSetting.max_category_nesting = 3
|
|
|
|
subcategory = Fabricate(:category, user: admin, parent_category: category)
|
|
|
|
subsubcategory = Fabricate(:category, user: admin, parent_category: subcategory)
|
|
|
|
|
|
|
|
topic1 = Fabricate(:topic, category: category)
|
|
|
|
topic2 = Fabricate(:topic, category: subcategory)
|
|
|
|
topic3 = Fabricate(:topic, category: subsubcategory)
|
|
|
|
CategoryFeaturedTopic.feature_topics
|
|
|
|
|
|
|
|
get "/categories.json?include_subcategories=true&include_topics=true"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
category_list = response.parsed_body["category_list"]
|
|
|
|
|
|
|
|
category_response = category_list["categories"].find { |c| c["id"] == category.id }
|
|
|
|
expect(category_response["topics"].map { |c| c['id'] }).to contain_exactly(topic1.id)
|
|
|
|
|
|
|
|
subcategory_response = category_response["subcategory_list"][0]
|
|
|
|
expect(subcategory_response["topics"].map { |c| c['id'] }).to contain_exactly(topic2.id)
|
|
|
|
|
|
|
|
subsubcategory_response = subcategory_response["subcategory_list"][0]
|
|
|
|
expect(subsubcategory_response["topics"].map { |c| c['id'] }).to contain_exactly(topic3.id)
|
|
|
|
end
|
|
|
|
|
2022-07-25 21:41:43 +08:00
|
|
|
describe 'categories and latest topics - ordered by created date' do
|
|
|
|
fab!(:category) { Fabricate(:category) }
|
|
|
|
fab!(:topic1) { Fabricate(:topic, category: category, created_at: 5.days.ago, updated_at: Time.now, bumped_at: Time.now) }
|
|
|
|
fab!(:topic2) { Fabricate(:topic, category: category, created_at: 2.days.ago, bumped_at: 2.days.ago) }
|
|
|
|
fab!(:topic3) { Fabricate(:topic, category: category, created_at: 1.day.ago, bumped_at: 1.day.ago) }
|
|
|
|
|
|
|
|
context 'when order is not set to created date' do
|
|
|
|
before do
|
|
|
|
SiteSetting.desktop_category_page_style = "categories_and_latest_topics"
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sorts topics by the default bump date' do
|
|
|
|
get "/categories_and_latest.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.parsed_body['topic_list']['topics'].map { |t| t["id"] }).to eq([topic1.id, topic3.id, topic2.id])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when order is set to created' do
|
|
|
|
before do
|
|
|
|
SiteSetting.desktop_category_page_style = "categories_and_latest_topics_created_date"
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sorts topics by crated at date' do
|
|
|
|
get "/categories_and_latest.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.parsed_body['topic_list']['topics'].map { |t| t["id"] }).to eq([topic3.id, topic2.id, topic1.id])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-03-05 05:11:59 +08:00
|
|
|
it 'includes subcategories and topics by default when view is subcategories_with_featured_topics' do
|
|
|
|
SiteSetting.max_category_nesting = 3
|
|
|
|
subcategory = Fabricate(:category, user: admin, parent_category: category)
|
|
|
|
|
|
|
|
topic1 = Fabricate(:topic, category: category)
|
|
|
|
CategoryFeaturedTopic.feature_topics
|
|
|
|
|
|
|
|
SiteSetting.desktop_category_page_style = "subcategories_with_featured_topics"
|
|
|
|
get "/categories.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
category_list = response.parsed_body["category_list"]
|
|
|
|
|
|
|
|
category_response = category_list["categories"].find { |c| c["id"] == category.id }
|
|
|
|
expect(category_response["topics"].map { |c| c['id'] }).to contain_exactly(topic1.id)
|
|
|
|
|
|
|
|
expect(category_response["subcategory_list"][0]["id"]).to eq(subcategory.id)
|
|
|
|
end
|
|
|
|
|
2022-03-15 06:23:39 +08:00
|
|
|
it "does not n+1 with multiple topics" do
|
|
|
|
category1 = Fabricate(:category)
|
|
|
|
category2 = Fabricate(:category)
|
|
|
|
topic1 = Fabricate(:topic, category: category1)
|
|
|
|
|
|
|
|
CategoryFeaturedTopic.feature_topics
|
|
|
|
SiteSetting.desktop_category_page_style = "categories_with_featured_topics"
|
|
|
|
|
|
|
|
# warmup
|
|
|
|
get "/categories.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
|
|
|
|
first_request_queries = track_sql_queries do
|
|
|
|
get "/categories.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
end
|
|
|
|
|
|
|
|
category_response = response.parsed_body["category_list"]["categories"].find { |c| c["id"] == category1.id }
|
|
|
|
expect(category_response["topics"].count).to eq(1)
|
|
|
|
|
|
|
|
topic2 = Fabricate(:topic, category: category2)
|
|
|
|
CategoryFeaturedTopic.feature_topics
|
|
|
|
|
|
|
|
second_request_queries = track_sql_queries do
|
|
|
|
get "/categories.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
end
|
|
|
|
|
|
|
|
category1_response = response.parsed_body["category_list"]["categories"].find { |c| c["id"] == category1.id }
|
|
|
|
category2_response = response.parsed_body["category_list"]["categories"].find { |c| c["id"] == category2.id }
|
|
|
|
expect(category1_response["topics"].size).to eq(1)
|
|
|
|
expect(category2_response["topics"].size).to eq(1)
|
|
|
|
|
|
|
|
expect(first_request_queries.count).to eq(second_request_queries.count)
|
|
|
|
end
|
|
|
|
|
2021-05-04 11:05:08 +08:00
|
|
|
it 'does not show uncategorized unless allow_uncategorized_topics' do
|
|
|
|
SiteSetting.desktop_category_page_style = "categories_boxes_with_topics"
|
|
|
|
|
|
|
|
uncategorized = Category.find(SiteSetting.uncategorized_category_id)
|
|
|
|
Fabricate(:topic, category: uncategorized)
|
|
|
|
CategoryFeaturedTopic.feature_topics
|
|
|
|
|
|
|
|
SiteSetting.allow_uncategorized_topics = false
|
|
|
|
|
|
|
|
get "/categories.json"
|
|
|
|
expect(response.parsed_body["category_list"]["categories"].map { |x| x['id'] }).not_to include(uncategorized.id)
|
|
|
|
end
|
2018-03-27 14:23:35 +08:00
|
|
|
end
|
|
|
|
|
2018-04-24 07:54:51 +08:00
|
|
|
context 'extensibility event' do
|
|
|
|
before do
|
|
|
|
sign_in(admin)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "triggers a extensibility event" do
|
|
|
|
event = DiscourseEvent.track_events {
|
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
name: 'hello',
|
|
|
|
color: 'ff0',
|
|
|
|
text_color: 'fff'
|
|
|
|
}
|
|
|
|
}.last
|
|
|
|
|
|
|
|
expect(event[:event_name]).to eq(:category_updated)
|
|
|
|
expect(event[:params].first).to eq(category)
|
|
|
|
end
|
2018-03-27 14:23:35 +08:00
|
|
|
end
|
2018-06-04 12:04:32 +08:00
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
describe '#create' do
|
2018-06-04 12:04:32 +08:00
|
|
|
it "requires the user to be logged in" do
|
|
|
|
post "/categories.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "logged in" do
|
|
|
|
before do
|
2019-03-14 22:47:38 +08:00
|
|
|
Jobs.run_immediately!
|
2018-06-04 12:04:32 +08:00
|
|
|
sign_in(admin)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an exception when they don't have permission to create it" do
|
|
|
|
sign_in(Fabricate(:user))
|
|
|
|
post "/categories.json", params: {
|
|
|
|
name: 'hello', color: 'ff0', text_color: 'fff'
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(response).to be_forbidden
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an exception when the name is missing" do
|
|
|
|
post "/categories.json", params: { color: "ff0", text_color: "fff" }
|
|
|
|
expect(response.status).to eq(400)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "failure" do
|
|
|
|
it "returns errors on a duplicate category name" do
|
|
|
|
category = Fabricate(:category, user: admin)
|
|
|
|
|
|
|
|
post "/categories.json", params: {
|
|
|
|
name: category.name, color: "ff0", text_color: "fff"
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
end
|
2018-08-31 04:28:40 +08:00
|
|
|
|
|
|
|
it "returns errors with invalid group" do
|
|
|
|
category = Fabricate(:category, user: admin)
|
|
|
|
readonly = CategoryGroup.permission_types[:readonly]
|
|
|
|
|
|
|
|
post "/categories.json", params: {
|
2018-08-31 04:39:40 +08:00
|
|
|
name: category.name, color: "ff0", text_color: "fff", permissions: { "invalid_group" => readonly }
|
2018-08-31 04:28:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(422)
|
2020-05-07 23:04:12 +08:00
|
|
|
expect(response.parsed_body['errors']).to be_present
|
2018-08-31 04:28:40 +08:00
|
|
|
end
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
describe "success" do
|
|
|
|
it "works" do
|
2020-07-15 00:36:19 +08:00
|
|
|
SiteSetting.enable_category_group_moderation = true
|
2019-04-18 05:12:32 +08:00
|
|
|
|
2018-06-04 12:04:32 +08:00
|
|
|
readonly = CategoryGroup.permission_types[:readonly]
|
|
|
|
create_post = CategoryGroup.permission_types[:create_post]
|
2019-04-18 05:12:32 +08:00
|
|
|
group = Fabricate(:group)
|
2018-06-04 12:04:32 +08:00
|
|
|
|
|
|
|
post "/categories.json", params: {
|
|
|
|
name: "hello",
|
|
|
|
color: "ff0",
|
|
|
|
text_color: "fff",
|
|
|
|
slug: "hello-cat",
|
|
|
|
auto_close_hours: 72,
|
2019-03-18 15:25:45 +08:00
|
|
|
search_priority: Searchable::PRIORITIES[:ignore],
|
2019-04-18 05:12:32 +08:00
|
|
|
reviewable_by_group_name: group.name,
|
2018-06-04 12:04:32 +08:00
|
|
|
permissions: {
|
|
|
|
"everyone" => readonly,
|
|
|
|
"staff" => create_post
|
2019-01-10 09:37:21 +08:00
|
|
|
}
|
2018-06-04 12:04:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
2020-05-07 23:04:12 +08:00
|
|
|
cat_json = response.parsed_body['category']
|
2019-04-18 05:12:32 +08:00
|
|
|
expect(cat_json).to be_present
|
|
|
|
expect(cat_json['reviewable_by_group_name']).to eq(group.name)
|
|
|
|
expect(cat_json['name']).to eq('hello')
|
|
|
|
expect(cat_json['slug']).to eq('hello-cat')
|
|
|
|
expect(cat_json['color']).to eq('ff0')
|
|
|
|
expect(cat_json['auto_close_hours']).to eq(72)
|
|
|
|
expect(cat_json['search_priority']).to eq(Searchable::PRIORITIES[:ignore])
|
|
|
|
|
|
|
|
category = Category.find(cat_json['id'])
|
2018-06-04 12:04:32 +08:00
|
|
|
expect(category.category_groups.map { |g| [g.group_id, g.permission_type] }.sort).to eq([
|
|
|
|
[Group[:everyone].id, readonly], [Group[:staff].id, create_post]
|
|
|
|
])
|
|
|
|
expect(UserHistory.count).to eq(4) # 1 + 3 (bootstrap mode)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
describe '#show' do
|
2019-08-19 15:08:28 +08:00
|
|
|
before do
|
|
|
|
category.set_permissions(admins: :full)
|
|
|
|
category.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
it "requires the user to be logged in" do
|
|
|
|
get "/c/#{category.id}/show.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "logged in" do
|
|
|
|
it "raises an exception if they don't have permission to see it" do
|
2022-05-10 23:02:28 +08:00
|
|
|
admin.update!(admin: false, group_users: [])
|
2019-08-19 15:08:28 +08:00
|
|
|
sign_in(admin)
|
|
|
|
get "/c/#{category.id}/show.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "renders category for users that have permission" do
|
|
|
|
sign_in(admin)
|
|
|
|
get "/c/#{category.id}/show.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
describe '#destroy' do
|
2018-06-04 12:04:32 +08:00
|
|
|
it "requires the user to be logged in" do
|
|
|
|
delete "/categories/category.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "logged in" do
|
|
|
|
it "raises an exception if they don't have permission to delete it" do
|
|
|
|
admin.update!(admin: false)
|
|
|
|
sign_in(admin)
|
|
|
|
delete "/categories/#{category.slug}.json"
|
|
|
|
expect(response).to be_forbidden
|
|
|
|
end
|
|
|
|
|
|
|
|
it "deletes the record" do
|
|
|
|
sign_in(admin)
|
2020-03-24 13:59:42 +08:00
|
|
|
|
|
|
|
id = Fabricate(:topic_timer, category: category).id
|
|
|
|
|
2018-06-04 12:04:32 +08:00
|
|
|
expect do
|
|
|
|
delete "/categories/#{category.slug}.json"
|
|
|
|
end.to change(Category, :count).by(-1)
|
2018-06-07 16:11:09 +08:00
|
|
|
expect(response.status).to eq(200)
|
2018-06-04 12:04:32 +08:00
|
|
|
expect(UserHistory.count).to eq(1)
|
2020-03-24 13:59:42 +08:00
|
|
|
expect(TopicTimer.where(id: id).exists?).to eq(false)
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
describe '#reorder' do
|
2018-06-04 12:04:32 +08:00
|
|
|
it "reorders the categories" do
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
c1 = category
|
|
|
|
c2 = Fabricate(:category)
|
|
|
|
c3 = Fabricate(:category)
|
|
|
|
c4 = Fabricate(:category)
|
|
|
|
if c3.id < c2.id
|
2018-12-04 11:48:13 +08:00
|
|
|
tmp = c3; c2 = c3; c3 = tmp
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
c1.position = 8
|
|
|
|
c2.position = 6
|
|
|
|
c3.position = 7
|
|
|
|
c4.position = 5
|
|
|
|
|
|
|
|
payload = {}
|
|
|
|
payload[c1.id] = 4
|
|
|
|
payload[c2.id] = 6
|
|
|
|
payload[c3.id] = 6
|
|
|
|
payload[c4.id] = 5
|
|
|
|
|
|
|
|
post "/categories/reorder.json", params: { mapping: MultiJson.dump(payload) }
|
|
|
|
|
|
|
|
SiteSetting.fixed_category_positions = true
|
|
|
|
list = CategoryList.new(Guardian.new(admin))
|
|
|
|
|
|
|
|
expect(list.categories).to eq([
|
|
|
|
Category.find(SiteSetting.uncategorized_category_id),
|
|
|
|
c1,
|
|
|
|
c4,
|
|
|
|
c2,
|
|
|
|
c3
|
|
|
|
])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
describe '#update' do
|
2018-06-04 12:04:32 +08:00
|
|
|
before do
|
2019-03-14 22:47:38 +08:00
|
|
|
Jobs.run_immediately!
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it "requires the user to be logged in" do
|
|
|
|
put "/categories/category.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "logged in" do
|
|
|
|
before do
|
|
|
|
sign_in(admin)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "raises an exception if they don't have permission to edit it" do
|
|
|
|
sign_in(Fabricate(:user))
|
|
|
|
put "/categories/#{category.slug}.json", params: {
|
|
|
|
name: 'hello',
|
|
|
|
color: 'ff0',
|
|
|
|
text_color: 'fff'
|
|
|
|
}
|
|
|
|
expect(response).to be_forbidden
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns errors on a duplicate category name" do
|
|
|
|
other_category = Fabricate(:category, name: "Other", user: admin)
|
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
name: other_category.name,
|
|
|
|
color: "ff0",
|
|
|
|
text_color: "fff",
|
|
|
|
}
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
end
|
|
|
|
|
2020-10-05 17:50:05 +08:00
|
|
|
it "returns errors when there is a name conflict while moving a category into another" do
|
|
|
|
parent_category = Fabricate(:category, name: "Parent", user: admin)
|
|
|
|
other_category = Fabricate(:category, name: category.name, user: admin, parent_category: parent_category, slug: "a-different-slug")
|
|
|
|
|
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
parent_category_id: parent_category.id,
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
end
|
|
|
|
|
2018-06-04 12:04:32 +08:00
|
|
|
it "returns 422 if email_in address is already in use for other category" do
|
2021-05-21 09:43:47 +08:00
|
|
|
_other_category = Fabricate(:category, name: "Other", email_in: "mail@example.com")
|
2018-06-04 12:04:32 +08:00
|
|
|
|
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
name: "Email",
|
2021-05-21 09:43:47 +08:00
|
|
|
email_in: "mail@example.com",
|
2018-06-04 12:04:32 +08:00
|
|
|
color: "ff0",
|
|
|
|
text_color: "fff",
|
|
|
|
}
|
|
|
|
expect(response.status).to eq(422)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "success" do
|
2019-04-05 04:35:31 +08:00
|
|
|
it "updates attributes correctly" do
|
2019-11-19 04:54:00 +08:00
|
|
|
SiteSetting.tagging_enabled = true
|
2018-06-04 12:04:32 +08:00
|
|
|
readonly = CategoryGroup.permission_types[:readonly]
|
|
|
|
create_post = CategoryGroup.permission_types[:create_post]
|
2019-10-31 02:49:00 +08:00
|
|
|
tag_group = Fabricate(:tag_group)
|
2018-06-04 12:04:32 +08:00
|
|
|
|
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
name: "hello",
|
|
|
|
color: "ff0",
|
|
|
|
text_color: "fff",
|
|
|
|
slug: "hello-category",
|
|
|
|
auto_close_hours: 72,
|
|
|
|
permissions: {
|
|
|
|
"everyone" => readonly,
|
|
|
|
"staff" => create_post
|
|
|
|
},
|
|
|
|
custom_fields: {
|
2022-07-28 10:23:35 +08:00
|
|
|
"dancing" => "frogs",
|
|
|
|
"running" => ["turtle", "salamander"]
|
2018-06-04 12:04:32 +08:00
|
|
|
},
|
2019-04-05 04:35:31 +08:00
|
|
|
minimum_required_tags: "",
|
2019-10-31 02:49:00 +08:00
|
|
|
allow_global_tags: 'true',
|
2022-04-06 21:08:06 +08:00
|
|
|
required_tag_groups: [{
|
|
|
|
name: tag_group.name,
|
|
|
|
min_count: 2
|
|
|
|
}]
|
2018-06-04 12:04:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
category.reload
|
|
|
|
expect(category.category_groups.map { |g| [g.group_id, g.permission_type] }.sort).to eq([
|
|
|
|
[Group[:everyone].id, readonly], [Group[:staff].id, create_post]
|
|
|
|
])
|
|
|
|
expect(category.name).to eq("hello")
|
|
|
|
expect(category.slug).to eq("hello-category")
|
|
|
|
expect(category.color).to eq("ff0")
|
|
|
|
expect(category.auto_close_hours).to eq(72)
|
2022-07-28 10:23:35 +08:00
|
|
|
expect(category.custom_fields).to eq("dancing" => "frogs", "running" => ["turtle", "salamander"])
|
2018-11-30 01:10:14 +08:00
|
|
|
expect(category.minimum_required_tags).to eq(0)
|
2019-04-05 04:35:31 +08:00
|
|
|
expect(category.allow_global_tags).to eq(true)
|
2022-04-06 21:08:06 +08:00
|
|
|
expect(category.category_required_tag_groups.count).to eq(1)
|
|
|
|
expect(category.category_required_tag_groups.first.tag_group.id).to eq(tag_group.id)
|
|
|
|
expect(category.category_required_tag_groups.first.min_count).to eq(2)
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'logs the changes correctly' do
|
|
|
|
category.update!(permissions: { "admins" => CategoryGroup.permission_types[:create_post] })
|
|
|
|
|
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
name: 'new name',
|
|
|
|
color: category.color,
|
|
|
|
text_color: category.text_color,
|
|
|
|
slug: category.slug,
|
|
|
|
permissions: {
|
|
|
|
"everyone" => CategoryGroup.permission_types[:create_post]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(UserHistory.count).to eq(5) # 2 + 3 (bootstrap mode)
|
|
|
|
end
|
2018-07-13 10:51:08 +08:00
|
|
|
|
2018-07-16 16:10:22 +08:00
|
|
|
it 'updates per-category settings correctly' do
|
2018-07-13 10:51:08 +08:00
|
|
|
category.custom_fields[Category::REQUIRE_TOPIC_APPROVAL] = false
|
|
|
|
category.custom_fields[Category::REQUIRE_REPLY_APPROVAL] = false
|
2018-07-16 16:10:22 +08:00
|
|
|
category.custom_fields[Category::NUM_AUTO_BUMP_DAILY] = 0
|
|
|
|
|
|
|
|
category.navigate_to_first_post_after_read = false
|
2018-07-13 10:51:08 +08:00
|
|
|
category.save!
|
|
|
|
|
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
name: category.name,
|
|
|
|
color: category.color,
|
|
|
|
text_color: category.text_color,
|
2018-07-16 16:10:22 +08:00
|
|
|
navigate_to_first_post_after_read: true,
|
2018-07-13 10:51:08 +08:00
|
|
|
custom_fields: {
|
|
|
|
require_reply_approval: true,
|
|
|
|
require_topic_approval: true,
|
2018-07-16 16:10:22 +08:00
|
|
|
num_auto_bump_daily: 10
|
2018-07-13 10:51:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
category.reload
|
|
|
|
expect(category.require_topic_approval?).to eq(true)
|
|
|
|
expect(category.require_reply_approval?).to eq(true)
|
2018-07-16 16:10:22 +08:00
|
|
|
expect(category.num_auto_bump_daily).to eq(10)
|
|
|
|
expect(category.navigate_to_first_post_after_read).to eq(true)
|
2018-07-13 10:51:08 +08:00
|
|
|
end
|
2019-11-19 04:54:00 +08:00
|
|
|
|
|
|
|
it "can remove required tag group" do
|
|
|
|
SiteSetting.tagging_enabled = true
|
2022-04-06 21:08:06 +08:00
|
|
|
category.update!(category_required_tag_groups: [ CategoryRequiredTagGroup.new(tag_group: Fabricate(:tag_group)) ])
|
2019-11-19 04:54:00 +08:00
|
|
|
put "/categories/#{category.id}.json", params: {
|
|
|
|
name: category.name,
|
|
|
|
color: category.color,
|
|
|
|
text_color: category.text_color,
|
|
|
|
allow_global_tags: 'false',
|
2021-09-14 20:04:54 +08:00
|
|
|
min_tags_from_required_group: 1,
|
2022-04-06 21:08:06 +08:00
|
|
|
required_tag_groups: []
|
2019-11-19 04:54:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
category.reload
|
2022-04-06 21:08:06 +08:00
|
|
|
expect(category.category_required_tag_groups).to be_empty
|
2019-11-19 04:54:00 +08:00
|
|
|
end
|
2021-09-14 20:04:54 +08:00
|
|
|
|
|
|
|
it "does not update other fields" do
|
|
|
|
SiteSetting.tagging_enabled = true
|
|
|
|
tag_group_1 = Fabricate(:tag_group)
|
|
|
|
tag_group_2 = Fabricate(:tag_group)
|
|
|
|
|
|
|
|
category.update!(
|
|
|
|
allowed_tags: ["hello", "world"],
|
|
|
|
allowed_tag_groups: [tag_group_1.name],
|
2022-04-06 21:08:06 +08:00
|
|
|
category_required_tag_groups: [ CategoryRequiredTagGroup.new(tag_group: tag_group_2) ],
|
2021-09-17 18:37:56 +08:00
|
|
|
custom_fields: { field_1: 'hello', field_2: 'hello' }
|
2021-09-14 20:04:54 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
put "/categories/#{category.id}.json"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
category.reload
|
|
|
|
expect(category.tags.pluck(:name)).to contain_exactly("hello", "world")
|
|
|
|
expect(category.tag_groups.pluck(:name)).to contain_exactly(tag_group_1.name)
|
2022-04-06 21:08:06 +08:00
|
|
|
expect(category.category_required_tag_groups.first.tag_group).to eq(tag_group_2)
|
2021-09-17 18:37:56 +08:00
|
|
|
expect(category.custom_fields).to eq({ 'field_1' => 'hello', 'field_2' => 'hello' })
|
2021-09-14 20:04:54 +08:00
|
|
|
|
2021-09-17 18:37:56 +08:00
|
|
|
put "/categories/#{category.id}.json", params: { allowed_tags: [], custom_fields: { field_1: nil } }
|
2021-09-14 20:04:54 +08:00
|
|
|
expect(response.status).to eq(200)
|
|
|
|
category.reload
|
|
|
|
expect(category.tags).to be_blank
|
|
|
|
expect(category.tag_groups.pluck(:name)).to contain_exactly(tag_group_1.name)
|
2022-04-06 21:08:06 +08:00
|
|
|
expect(category.category_required_tag_groups.first.tag_group).to eq(tag_group_2)
|
2021-09-17 18:37:56 +08:00
|
|
|
expect(category.custom_fields).to eq({ 'field_2' => 'hello' })
|
2021-09-14 20:04:54 +08:00
|
|
|
|
2022-04-06 21:08:06 +08:00
|
|
|
put "/categories/#{category.id}.json", params: { allowed_tags: [], allowed_tag_groups: [], required_tag_groups: [], custom_fields: { field_1: 'hi', field_2: nil } }
|
2021-09-14 20:04:54 +08:00
|
|
|
expect(response.status).to eq(200)
|
|
|
|
category.reload
|
|
|
|
expect(category.tags).to be_blank
|
|
|
|
expect(category.tag_groups).to be_blank
|
2022-04-06 21:08:06 +08:00
|
|
|
expect(category.category_required_tag_groups).to eq([])
|
2021-09-17 18:37:56 +08:00
|
|
|
expect(category.custom_fields).to eq({ 'field_1' => 'hi' })
|
2021-09-14 20:04:54 +08:00
|
|
|
end
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
describe '#update_slug' do
|
2018-06-04 12:04:32 +08:00
|
|
|
it 'requires the user to be logged in' do
|
|
|
|
put "/category/category/slug.json"
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'logged in' do
|
|
|
|
before do
|
|
|
|
sign_in(admin)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects blank' do
|
2020-04-21 09:50:20 +08:00
|
|
|
put "/category/#{category.id}/slug.json", params: { slug: ' ' }
|
2018-06-04 12:04:32 +08:00
|
|
|
expect(response.status).to eq(422)
|
2020-04-21 09:50:20 +08:00
|
|
|
expect(response.parsed_body["errors"]).to eq(["Slug can't be blank"])
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts valid custom slug' do
|
|
|
|
put "/category/#{category.id}/slug.json", params: { slug: 'valid-slug' }
|
|
|
|
|
2018-06-07 16:11:09 +08:00
|
|
|
expect(response.status).to eq(200)
|
2018-06-04 12:04:32 +08:00
|
|
|
expect(category.reload.slug).to eq('valid-slug')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts not well formed custom slug' do
|
|
|
|
put "/category/#{category.id}/slug.json", params: { slug: ' valid slug' }
|
|
|
|
|
2018-06-07 16:11:09 +08:00
|
|
|
expect(response.status).to eq(200)
|
2018-06-04 12:04:32 +08:00
|
|
|
expect(category.reload.slug).to eq('valid-slug')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts and sanitize custom slug when the slug generation method is not ascii' do
|
|
|
|
SiteSetting.slug_generation_method = 'none'
|
|
|
|
put "/category/#{category.id}/slug.json", params: { slug: ' another !_ slug @' }
|
|
|
|
|
2018-06-07 16:11:09 +08:00
|
|
|
expect(response.status).to eq(200)
|
2018-06-04 12:04:32 +08:00
|
|
|
expect(category.reload.slug).to eq('another-slug')
|
|
|
|
SiteSetting.slug_generation_method = 'ascii'
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects invalid custom slug' do
|
2020-04-21 09:50:50 +08:00
|
|
|
put "/category/#{category.id}/slug.json", params: { slug: '.' }
|
2018-06-04 12:04:32 +08:00
|
|
|
expect(response.status).to eq(422)
|
2020-04-21 09:50:50 +08:00
|
|
|
expect(response.parsed_body["errors"]).to eq(["Slug is invalid"])
|
2018-06-04 12:04:32 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-01-30 02:30:48 +08:00
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
describe '#categories_and_topics' do
|
2020-01-30 02:30:48 +08:00
|
|
|
before do
|
|
|
|
10.times.each { Fabricate(:topic) }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'works when SiteSetting.categories_topics is non-null' do
|
|
|
|
SiteSetting.categories_topics = 5
|
|
|
|
|
|
|
|
get '/categories_and_latest.json'
|
2020-05-07 23:04:12 +08:00
|
|
|
expect(response.parsed_body['topic_list']['topics'].size).to eq(5)
|
2020-01-30 02:30:48 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'works when SiteSetting.categories_topics is null' do
|
|
|
|
SiteSetting.categories_topics = 0
|
|
|
|
|
|
|
|
get '/categories_and_latest.json'
|
2020-05-07 23:04:12 +08:00
|
|
|
json = response.parsed_body
|
2020-05-15 10:41:40 +08:00
|
|
|
|
|
|
|
category_list = json['category_list']
|
|
|
|
topic_list = json['topic_list']
|
|
|
|
|
|
|
|
expect(category_list['categories'].size).to eq(2) # 'Uncategorized' and category
|
|
|
|
expect(topic_list['topics'].size).to eq(5)
|
2020-01-30 02:30:48 +08:00
|
|
|
|
|
|
|
Fabricate(:category, parent_category: category)
|
|
|
|
|
|
|
|
get '/categories_and_latest.json'
|
2020-05-07 23:04:12 +08:00
|
|
|
json = response.parsed_body
|
2020-01-30 02:30:48 +08:00
|
|
|
expect(json['category_list']['categories'].size).to eq(2)
|
|
|
|
expect(json['topic_list']['topics'].size).to eq(5)
|
|
|
|
|
|
|
|
Fabricate(:category)
|
|
|
|
Fabricate(:category)
|
|
|
|
|
|
|
|
get '/categories_and_latest.json'
|
2020-05-07 23:04:12 +08:00
|
|
|
json = response.parsed_body
|
2020-01-30 02:30:48 +08:00
|
|
|
expect(json['category_list']['categories'].size).to eq(4)
|
|
|
|
expect(json['topic_list']['topics'].size).to eq(6)
|
|
|
|
end
|
2021-05-04 11:05:08 +08:00
|
|
|
|
|
|
|
it 'does not show uncategorized unless allow_uncategorized_topics' do
|
|
|
|
uncategorized = Category.find(SiteSetting.uncategorized_category_id)
|
|
|
|
Fabricate(:topic, category: uncategorized)
|
|
|
|
CategoryFeaturedTopic.feature_topics
|
|
|
|
|
|
|
|
SiteSetting.allow_uncategorized_topics = false
|
|
|
|
|
|
|
|
get "/categories_and_latest.json"
|
|
|
|
expect(response.parsed_body["category_list"]["categories"].map { |x| x['id'] }).not_to include(uncategorized.id)
|
|
|
|
end
|
2021-11-12 00:35:03 +08:00
|
|
|
|
|
|
|
describe 'Showing top topics from private categories' do
|
|
|
|
it 'returns the top topic from the private category when the user is a member' do
|
|
|
|
restricted_group = Fabricate(:group)
|
|
|
|
private_cat = Fabricate(:private_category, group: restricted_group)
|
|
|
|
private_topic = Fabricate(:topic, category: private_cat, like_count: 1000, posts_count: 100)
|
|
|
|
TopTopic.refresh!
|
|
|
|
restricted_group.add(user)
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
get "/categories_and_top.json"
|
|
|
|
parsed_topic = response.parsed_body.dig('topic_list', 'topics').detect do |t|
|
|
|
|
t.dig('id') == private_topic.id
|
|
|
|
end
|
|
|
|
|
|
|
|
expect(parsed_topic).to be_present
|
|
|
|
end
|
|
|
|
end
|
2020-01-30 02:30:48 +08:00
|
|
|
end
|
2022-04-08 11:14:06 +08:00
|
|
|
|
|
|
|
describe '#visible_groups' do
|
|
|
|
fab!(:public_group) { Fabricate(:group, visibility_level: Group.visibility_levels[:public], name: 'aaa') }
|
|
|
|
fab!(:private_group) { Fabricate(:group, visibility_level: Group.visibility_levels[:staff], name: 'bbb') }
|
|
|
|
fab!(:user_only_group) { Fabricate(:group, visibility_level: Group.visibility_levels[:members], name: 'ccc') }
|
|
|
|
|
|
|
|
it 'responds with 404 when id param is invalid' do
|
|
|
|
get "/c/-9999/visible_groups.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(404)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "responds with 403 when category is restricted to the current user" do
|
|
|
|
category.set_permissions(private_group.name => :full)
|
|
|
|
category.save!
|
|
|
|
|
|
|
|
get "/c/#{category.id}/visible_groups.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(403)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns the names of the groups that are visible to an admin" do
|
|
|
|
sign_in(admin)
|
|
|
|
|
|
|
|
category.set_permissions(
|
|
|
|
private_group.name => :full,
|
|
|
|
public_group.name => :full,
|
|
|
|
user_only_group.name => :full,
|
|
|
|
)
|
|
|
|
|
|
|
|
category.save!
|
|
|
|
|
|
|
|
get "/c/#{category.id}/visible_groups.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.parsed_body["groups"]).to eq([public_group.name, private_group.name, user_only_group.name])
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns the names of the groups that are visible to a user and excludes the everyone group" do
|
2022-04-18 16:16:30 +08:00
|
|
|
private_group.add(user)
|
2022-04-08 11:14:06 +08:00
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
category.set_permissions(
|
|
|
|
private_group.name => :full,
|
|
|
|
public_group.name => :full,
|
|
|
|
user_only_group.name => :full,
|
|
|
|
)
|
|
|
|
|
|
|
|
category.save!
|
|
|
|
|
|
|
|
get "/c/#{category.id}/visible_groups.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.parsed_body["groups"]).to eq([public_group.name])
|
|
|
|
end
|
2022-04-18 16:16:30 +08:00
|
|
|
|
|
|
|
it "returns no groups if everyone can see it" do
|
|
|
|
sign_in(user)
|
|
|
|
|
|
|
|
category.set_permissions(
|
|
|
|
"everyone" => :readonly,
|
|
|
|
private_group.name => :full,
|
|
|
|
public_group.name => :full,
|
|
|
|
user_only_group.name => :full,
|
|
|
|
)
|
|
|
|
|
|
|
|
category.save!
|
|
|
|
|
|
|
|
get "/c/#{category.id}/visible_groups.json"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.parsed_body["groups"]).to eq([])
|
|
|
|
end
|
2022-04-08 11:14:06 +08:00
|
|
|
end
|
2018-03-27 14:23:35 +08:00
|
|
|
end
|