mirror of
https://github.com/discourse/discourse.git
synced 2025-05-29 11:48:08 +08:00

When replying to a post, the user who is getting the reply should be prioritized in the autocomplete - Added in composer a getter for getting `.replyingToUser` - Added in d-editor the reference to the user that is getting the reply(`this.composer.replyingToUser`) - Passed along the reference to the user that is getting the reply to the user-search service as `replyingToUser` - Controller `users_controller.rb` was modified to accept the `user_id` parameter and pass it to the `UserSearch` model - The `UserSearch` model was modified to accept the `user_id` parameter and use it to prioritize the user that is getting the reply in the autocomplete on the first time you call the autocomplete service and while the username is included in the searched term - Had to update the serializer to pass the id of the `replyingToUser` from the post
312 lines
12 KiB
Ruby
312 lines
12 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe UserSearch do
|
|
before_all { SearchIndexer.enable } # Enable for prefabrication
|
|
before { SearchIndexer.enable } # Enable for each test
|
|
|
|
fab!(:topic)
|
|
fab!(:topic2) { Fabricate :topic }
|
|
fab!(:topic3) { Fabricate :topic }
|
|
fab!(:topic4) { Fabricate :topic }
|
|
fab!(:mr_b) do
|
|
Fabricate :user, username: "mrb", name: "Michael Madsen", last_seen_at: 10.days.ago
|
|
end
|
|
fab!(:mr_blue) do
|
|
Fabricate :user, username: "mrblue", name: "Eddie Code", last_seen_at: 9.days.ago
|
|
end
|
|
fab!(:mr_orange) do
|
|
Fabricate :user, username: "mrorange", name: "Tim Roth", last_seen_at: 8.days.ago
|
|
end
|
|
fab!(:mr_pink) do
|
|
Fabricate :user, username: "mrpink", name: "Steve Buscemi", last_seen_at: 7.days.ago
|
|
end
|
|
fab!(:mr_brown) do
|
|
Fabricate :user, username: "mrbrown", name: "Quentin Tarantino", last_seen_at: 6.days.ago
|
|
end
|
|
fab!(:mr_white) do
|
|
Fabricate :user, username: "mrwhite", name: "Harvey Keitel", last_seen_at: 5.days.ago
|
|
end
|
|
fab!(:inactive) { Fabricate :user, username: "Ghost", active: false }
|
|
fab!(:admin) { Fabricate :admin, username: "theadmin" }
|
|
fab!(:moderator) { Fabricate :moderator, username: "themod" }
|
|
fab!(:staged)
|
|
|
|
def search_for(*args)
|
|
# mapping "username" so it's easier to debug
|
|
UserSearch.new(*args).search.map(&:username)
|
|
end
|
|
|
|
context "with a secure category" do
|
|
fab!(:user)
|
|
fab!(:searching_user) { Fabricate(:user) }
|
|
fab!(:group)
|
|
fab!(:category) { Fabricate(:category, read_restricted: true, user: user) }
|
|
|
|
before_all do
|
|
Fabricate(:category_group, category: category, group: group)
|
|
|
|
group.add(user)
|
|
group.add(searching_user)
|
|
group.save
|
|
end
|
|
|
|
it "autocompletes with people in the category" do
|
|
results = search_for("", searching_user: searching_user, category_id: category.id)
|
|
expect(results).to eq [user.username]
|
|
end
|
|
|
|
it "will lookup the category from the topic id" do
|
|
topic = Fabricate(:topic, category: category)
|
|
Fabricate(:post, user: topic.user, topic: topic)
|
|
|
|
results = search_for("", searching_user: searching_user, topic_id: topic.id)
|
|
|
|
expect(results).to eq [topic.user, user].map(&:username)
|
|
end
|
|
|
|
it "will raise an error if the user cannot see the category" do
|
|
expect do
|
|
search_for("", searching_user: Fabricate(:user), category_id: category.id)
|
|
end.to raise_error(Discourse::InvalidAccess)
|
|
end
|
|
|
|
it "will respect the group member visibility setting" do
|
|
group.update(members_visibility_level: Group.visibility_levels[:owners])
|
|
results = search_for("", searching_user: searching_user, category_id: category.id)
|
|
expect(results).to be_blank
|
|
|
|
group.add_owner(searching_user)
|
|
results = search_for("", searching_user: searching_user, category_id: category.id)
|
|
expect(results).to eq [user.username]
|
|
end
|
|
end
|
|
|
|
it "allows for correct underscore searching" do
|
|
Fabricate(:user, username: "undertaker")
|
|
under_score = Fabricate(:user, username: "Under_Score")
|
|
|
|
expect(search_for("under_sc")).to eq [under_score.username]
|
|
expect(search_for("under_")).to eq [under_score.username]
|
|
end
|
|
|
|
it "allows filtering by group" do
|
|
sam = Fabricate(:user, username: "sam")
|
|
Fabricate(:user, username: "samantha")
|
|
|
|
group = Fabricate(:group)
|
|
group.add(sam)
|
|
|
|
results = search_for("sam", groups: [group])
|
|
expect(results).to eq [sam.username]
|
|
end
|
|
|
|
it "allows filtering by multiple groups" do
|
|
sam = Fabricate(:user, username: "sam")
|
|
samantha = Fabricate(:user, username: "samantha")
|
|
|
|
group_1 = Fabricate(:group)
|
|
group_1.add(sam)
|
|
|
|
group_2 = Fabricate(:group)
|
|
group_2.add(samantha)
|
|
|
|
results = search_for("sam", groups: [group_1, group_2])
|
|
expect(results).to eq [sam, samantha].map(&:username)
|
|
end
|
|
|
|
context "with seed data" do
|
|
fab!(:post1) { Fabricate :post, user: mr_b, topic: topic }
|
|
fab!(:post2) { Fabricate :post, user: mr_blue, topic: topic2 }
|
|
fab!(:post3) { Fabricate :post, user: mr_orange, topic: topic }
|
|
fab!(:post4) { Fabricate :post, user: mr_pink, topic: topic }
|
|
fab!(:post5) { Fabricate :post, user: mr_brown, topic: topic3 }
|
|
fab!(:post6) { Fabricate :post, user: mr_white, topic: topic }
|
|
fab!(:post7) { Fabricate :post, user: staged, topic: topic4 }
|
|
fab!(:post8) { Fabricate :post, user: mr_brown, topic: topic2, post_type: Post.types[:whisper] }
|
|
|
|
before { mr_white.update(suspended_at: 1.day.ago, suspended_till: 1.year.from_now) }
|
|
|
|
it "can search by name and username" do
|
|
# normal search
|
|
results = search_for(mr_b.name.split.first)
|
|
expect(results).to eq [mr_b.username]
|
|
|
|
# lower case
|
|
results = search_for(mr_b.name.split.first.downcase)
|
|
expect(results).to eq [mr_b.username]
|
|
|
|
# username
|
|
results = search_for(mr_pink.username)
|
|
expect(results).to eq [mr_pink.username]
|
|
|
|
# case insensitive
|
|
results = search_for(mr_pink.username.upcase)
|
|
expect(results).to eq [mr_pink.username]
|
|
end
|
|
|
|
it "handles substring search correctly" do
|
|
results = search_for("mr")
|
|
expect(results).to eq [mr_brown, mr_pink, mr_orange, mr_blue, mr_b].map(&:username)
|
|
|
|
results = search_for("mr", searching_user: mr_b)
|
|
expect(results).to eq [mr_brown, mr_pink, mr_orange, mr_blue, mr_b].map(&:username)
|
|
|
|
# only staff members see suspended users in results
|
|
results = search_for("mr", searching_user: moderator)
|
|
expect(results).to eq [mr_white, mr_brown, mr_pink, mr_orange, mr_blue, mr_b].map(&:username)
|
|
|
|
results = search_for("mr", searching_user: admin)
|
|
expect(results).to eq [mr_white, mr_brown, mr_pink, mr_orange, mr_blue, mr_b].map(&:username)
|
|
|
|
results = search_for(mr_b.username, searching_user: admin)
|
|
expect(results).to eq [mr_b, mr_brown, mr_blue].map(&:username)
|
|
|
|
results = search_for("MR", searching_user: admin)
|
|
expect(results).to eq [mr_white, mr_brown, mr_pink, mr_orange, mr_blue, mr_b].map(&:username)
|
|
|
|
results = search_for("MRB", searching_user: admin, limit: 2)
|
|
expect(results).to eq [mr_b, mr_brown].map(&:username)
|
|
end
|
|
|
|
it "prioritises topic participants" do
|
|
results = search_for(mr_b.username, topic_id: topic.id)
|
|
expect(results).to eq [mr_b, mr_brown, mr_blue].map(&:username)
|
|
|
|
results = search_for(mr_b.username, topic_id: topic2.id)
|
|
expect(results).to eq [mr_b, mr_blue, mr_brown].map(&:username)
|
|
|
|
results = search_for(mr_b.username, topic_id: topic3.id)
|
|
expect(results).to eq [mr_b, mr_brown, mr_blue].map(&:username)
|
|
end
|
|
|
|
it "prioritises the replying to user within a topic" do
|
|
results = search_for("mr", topic_id: topic.id, prioritized_user_id: mr_b.id)
|
|
expect(results).to eq [mr_b, mr_pink, mr_orange, mr_brown, mr_blue].map(&:username)
|
|
|
|
results = search_for("mr", topic_id: topic.id, prioritized_user_id: mr_orange.id)
|
|
expect(results).to eq [mr_orange, mr_pink, mr_b, mr_brown, mr_blue].map(&:username)
|
|
|
|
results = search_for("mr", topic_id: topic.id, prioritized_user_id: mr_pink.id)
|
|
expect(results).to eq [mr_pink, mr_orange, mr_b, mr_brown, mr_blue].map(&:username)
|
|
end
|
|
|
|
it "returns the replying to user if the term includes the username" do
|
|
results = search_for(mr_blue.username, topic_id: topic.id, prioritized_user_id: post1.user_id)
|
|
expect(results).to eq [mr_blue].map(&:username)
|
|
results = search_for(mr_blue.username, topic_id: topic.id, prioritized_user_id: post3.user_id)
|
|
expect(results).to eq [mr_blue].map(&:username)
|
|
end
|
|
|
|
it "returns firstly the replying to user if the term is blank" do
|
|
results = search_for("", topic_id: topic.id, prioritized_user_id: post1.user_id)
|
|
expect(results).to eq [mr_b, mr_pink, mr_orange].map(&:username)
|
|
|
|
results = search_for("", topic_id: topic.id, prioritized_user_id: post3.user_id)
|
|
expect(results).to eq [mr_orange, mr_pink, mr_b].map(&:username)
|
|
|
|
results = search_for("", topic_id: topic.id, prioritized_user_id: post4.user_id)
|
|
expect(results).to eq [mr_pink, mr_orange, mr_b].map(&:username)
|
|
end
|
|
|
|
it "does not reveal whisper users" do
|
|
results = search_for("", topic_id: topic2.id)
|
|
expect(results).to eq [mr_blue.username]
|
|
end
|
|
|
|
it "does not include deleted posts users" do
|
|
post4.trash!
|
|
results = search_for("", topic_id: topic.id)
|
|
expect(results).to eq [mr_orange, mr_b].map(&:username)
|
|
end
|
|
|
|
it "only reveals topic participants to people with permission" do
|
|
pm_topic =
|
|
Fabricate(:private_message_post, user: Fabricate(:user, refresh_auto_groups: true)).topic
|
|
|
|
# Anonymous, does not have access
|
|
expect do search_for("", topic_id: pm_topic.id) end.to raise_error(Discourse::InvalidAccess)
|
|
|
|
# Random user, does not have access
|
|
expect do search_for("", topic_id: pm_topic.id, searching_user: mr_b) end.to raise_error(
|
|
Discourse::InvalidAccess,
|
|
)
|
|
|
|
pm_topic.invite(pm_topic.user, mr_b.username)
|
|
|
|
results = search_for("", topic_id: pm_topic.id, searching_user: mr_b)
|
|
expect(results).to eq [pm_topic.user.username]
|
|
end
|
|
|
|
it "only searches by name when enabled" do
|
|
# When searching by name is enabled, it returns the record
|
|
SiteSetting.enable_names = true
|
|
results = search_for("Tarantino")
|
|
expect(results).to eq [mr_brown.username]
|
|
|
|
results = search_for("coding")
|
|
expect(results).to be_blank
|
|
|
|
results = search_for("z")
|
|
expect(results).to be_blank
|
|
|
|
# When searching by name is disabled, it will not return the record
|
|
SiteSetting.enable_names = false
|
|
results = search_for("Tarantino")
|
|
expect(results).to be_blank
|
|
end
|
|
|
|
it "does not show unapproved users when must_approve_users enabled" do
|
|
SiteSetting.must_approve_users = true
|
|
unapproved = Fabricate(:user, username: "mrunapproved", active: true, approved: false)
|
|
approved = Fabricate(:user, username: "mrapproved", active: true, approved: true)
|
|
|
|
users = search_for(unapproved.username)
|
|
expect(users).to be_blank
|
|
|
|
users = search_for(approved.username)
|
|
expect(users).not_to be_blank
|
|
end
|
|
|
|
it "prioritises exact matches" do
|
|
results = search_for("mrB")
|
|
expect(results).to eq [mr_b, mr_brown, mr_blue].map(&:username)
|
|
end
|
|
|
|
it "doesn't prioritises exact matches mentions for users who haven't been seen in over a year" do
|
|
abcdef = Fabricate(:user, username: "abcdef", last_seen_at: 2.days.ago)
|
|
abcde = Fabricate(:user, username: "abcde", last_seen_at: 2.weeks.ago)
|
|
abcd = Fabricate(:user, username: "abcd", last_seen_at: 2.months.ago)
|
|
abc = Fabricate(:user, username: "abc", last_seen_at: 2.years.ago)
|
|
|
|
results = search_for("abc", topic_id: topic.id)
|
|
expect(results).to eq [abcdef, abcde, abcd, abc].map(&:username)
|
|
end
|
|
|
|
it "does not include self, staged or inactive" do
|
|
# don't return inactive users
|
|
results = search_for(inactive.username)
|
|
expect(results).to be_blank
|
|
|
|
# don't return staged users
|
|
results = search_for(staged.username)
|
|
expect(results).to be_blank
|
|
|
|
results = search_for(staged.username, include_staged_users: true)
|
|
expect(results).to eq [staged.username]
|
|
|
|
# mrb is omitted since they're the searching user
|
|
results = search_for("", topic_id: topic.id, searching_user: mr_b)
|
|
expect(results).to eq [mr_pink, mr_orange].map(&:username)
|
|
end
|
|
|
|
it "works with last_seen_users option" do
|
|
results = search_for("", last_seen_users: true)
|
|
|
|
expect(results).not_to be_blank
|
|
expect(results[0]).to eq("mrbrown")
|
|
expect(results[1]).to eq("mrpink")
|
|
expect(results[2]).to eq("mrorange")
|
|
end
|
|
end
|
|
end
|