mirror of
https://github.com/discourse/discourse.git
synced 2025-02-24 19:19:56 +08:00

Lazy loading images naturally causes a slight delay, because the browser only starts to load them after laying out the DOM and checking whether they're in the viewport. Plus, in Safari, re-rendering the DOM of a lazy-loaded image always causes a brief flicker, even if the image is already cached in the browser. Lazy-loading is most beneficial on large one-off images which are often rendered outside the viewport. That's frequently the case for images which users share in topics. Avatars, on the other hand, are very small images, they're very often above-the-fold, and the same avatar often occurs many times on the same page. Therefore, this commit removes `loading="lazy"` from avatars, which should improve avatar load times in all browsers, and stop the flicker in Safari. --- Tapping logo to reload topic-list in Safari. Before: https://github.com/user-attachments/assets/242299f8-aa13-4991-b321-2f143603ed26 After: https://github.com/user-attachments/assets/5e5bfd28-3a78-40fd-af21-3d92e7b3ba8a
841 lines
32 KiB
Ruby
841 lines
32 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe UsernameChanger do
|
|
before { Jobs.run_immediately! }
|
|
|
|
describe "#change" do
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
context "when everything goes well" do
|
|
let!(:old_username) { user.username }
|
|
|
|
it "should change the username" do
|
|
new_username = "#{user.username}1234"
|
|
|
|
events =
|
|
DiscourseEvent
|
|
.track_events { @result = UsernameChanger.change(user, new_username) }
|
|
.last(2)
|
|
|
|
expect(@result).to eq(true)
|
|
|
|
event = events.first
|
|
expect(event[:event_name]).to eq(:username_changed)
|
|
expect(event[:params].first).to eq(old_username)
|
|
expect(event[:params].second).to eq(new_username)
|
|
|
|
event = events.last
|
|
expect(event[:event_name]).to eq(:user_updated)
|
|
expect(event[:params].first).to eq(user)
|
|
|
|
user.reload
|
|
expect(user.username).to eq(new_username)
|
|
expect(user.username_lower).to eq(new_username.downcase)
|
|
end
|
|
|
|
it "do nothing if the new username is the same" do
|
|
new_username = user.username
|
|
|
|
events =
|
|
DiscourseEvent.track_events { @result = UsernameChanger.change(user, new_username) }
|
|
|
|
expect(@result).to eq(false)
|
|
expect(events.count).to be_zero
|
|
end
|
|
end
|
|
|
|
context "when something goes wrong" do
|
|
let(:wrong_username) { "" }
|
|
let(:username_before_change) { user.username }
|
|
let(:username_lower_before_change) { user.username_lower }
|
|
|
|
it "should not change the username" do
|
|
@result = UsernameChanger.change(user, wrong_username)
|
|
expect(@result).to eq(false)
|
|
|
|
user.reload
|
|
expect(user.username).to eq(username_before_change)
|
|
expect(user.username_lower).to eq(username_lower_before_change)
|
|
end
|
|
end
|
|
|
|
context "when changing the case of my username" do
|
|
let!(:myself) { Fabricate(:user, username: "hansolo") }
|
|
|
|
it "should change the username" do
|
|
expect do
|
|
expect(UsernameChanger.change(myself, "HanSolo", myself)).to eq(true)
|
|
end.to change { UserHistory.count }.by(1)
|
|
|
|
expect(UserHistory.last.action).to eq(UserHistory.actions[:change_username])
|
|
|
|
expect(myself.reload.username).to eq("HanSolo")
|
|
|
|
expect do UsernameChanger.change(myself, "HanSolo", myself) end.not_to change {
|
|
UserHistory.count
|
|
} # make sure it does not log a dupe
|
|
|
|
expect do UsernameChanger.change(myself, user.username, myself) end.not_to change {
|
|
UserHistory.count
|
|
} # does not log if the username already exists
|
|
end
|
|
end
|
|
|
|
describe "allow custom minimum username length from site settings" do
|
|
before do
|
|
@custom_min = 2
|
|
SiteSetting.min_username_length = @custom_min
|
|
end
|
|
|
|
it "should allow a shorter username than default" do
|
|
result = UsernameChanger.change(user, "a" * @custom_min)
|
|
expect(result).not_to eq(false)
|
|
end
|
|
|
|
it "should not allow a shorter username than limit" do
|
|
result = UsernameChanger.change(user, "a" * (@custom_min - 1))
|
|
expect(result).to eq(false)
|
|
end
|
|
|
|
it "should not allow a longer username than limit" do
|
|
result = UsernameChanger.change(user, "a" * (User.username_length.end + 1))
|
|
expect(result).to eq(false)
|
|
end
|
|
end
|
|
|
|
context "when there are posts and revisions" do
|
|
let(:user) { Fabricate(:user, username: "foo") }
|
|
let(:topic) { Fabricate(:topic, user: user) }
|
|
|
|
before do
|
|
UserActionManager.enable
|
|
Discourse.expects(:warn_exception).never
|
|
end
|
|
|
|
def create_post_and_change_username(args = {}, &block)
|
|
stub_image_size
|
|
post = create_post(args.merge(topic_id: topic.id))
|
|
|
|
args
|
|
.delete(:revisions)
|
|
&.each { |revision| post.revise(post.user, revision, force_new_version: true) }
|
|
|
|
block.call(post) if block
|
|
|
|
UsernameChanger.change(user, args[:target_username] || "bar")
|
|
post.reload
|
|
end
|
|
|
|
context "when there are mentions" do
|
|
it "rewrites cooked correctly" do
|
|
post = create_post_and_change_username(raw: "Hello @foo")
|
|
expect(post.cooked).to eq(%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a></p>))
|
|
|
|
post.rebake!
|
|
expect(post.cooked).to eq(%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a></p>))
|
|
end
|
|
|
|
it "removes the username from the search index" do
|
|
SearchIndexer.enable
|
|
create_post_and_change_username(raw: "Hello @foo")
|
|
|
|
results = Search.execute("foo", min_search_term_length: 1)
|
|
expect(results.posts).to be_empty
|
|
end
|
|
|
|
it "ignores case when replacing mentions" do
|
|
post = create_post_and_change_username(raw: "There's no difference between @foo and @Foo")
|
|
|
|
expect(post.raw).to eq("There's no difference between @bar and @bar")
|
|
expect(post.cooked).to eq(
|
|
%Q(<p>There’s no difference between <a class="mention" href="/u/bar">@bar</a> and <a class="mention" href="/u/bar">@bar</a></p>),
|
|
)
|
|
end
|
|
|
|
it "replaces mentions when there are leading symbols" do
|
|
post = create_post_and_change_username(raw: ".@foo -@foo %@foo _@foo ,@foo ;@foo @@foo")
|
|
|
|
expect(post.raw).to eq(".@bar -@bar %@bar _@bar ,@bar ;@bar @@bar")
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<p>.<a class="mention" href="/u/bar">@bar</a>
|
|
-<a class="mention" href="/u/bar">@bar</a>
|
|
%<a class="mention" href="/u/bar">@bar</a>
|
|
_<a class="mention" href="/u/bar">@bar</a>
|
|
,<a class="mention" href="/u/bar">@bar</a>
|
|
;<a class="mention" href="/u/bar">@bar</a>
|
|
@<a class="mention" href="/u/bar">@bar</a></p>
|
|
HTML
|
|
end
|
|
|
|
it "replaces mentions within double and single quotes" do
|
|
post = create_post_and_change_username(raw: %Q("@foo" '@foo'))
|
|
|
|
expect(post.raw).to eq(%Q("@bar" '@bar'))
|
|
expect(post.cooked).to eq(
|
|
%Q(<p>“<a class="mention" href="/u/bar">@bar</a>” ‘<a class="mention" href="/u/bar">@bar</a>’</p>),
|
|
)
|
|
end
|
|
|
|
it "replaces Markdown formatted mentions" do
|
|
post = create_post_and_change_username(raw: "**@foo** *@foo* _@foo_ ~~@foo~~")
|
|
|
|
expect(post.raw).to eq("**@bar** *@bar* _@bar_ ~~@bar~~")
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<p><strong><a class="mention" href="/u/bar">@bar</a></strong>
|
|
<em><a class="mention" href="/u/bar">@bar</a></em>
|
|
<em><a class="mention" href="/u/bar">@bar</a></em>
|
|
<s><a class="mention" href="/u/bar">@bar</a></s></p>
|
|
HTML
|
|
end
|
|
|
|
it "replaces mentions when there are trailing symbols" do
|
|
post = create_post_and_change_username(raw: "@foo. @foo, @foo: @foo; @foo_ @foo-")
|
|
|
|
expect(post.raw).to eq("@bar. @bar, @bar: @bar; @bar_ @bar-")
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<p><a class="mention" href="/u/bar">@bar</a>.
|
|
<a class="mention" href="/u/bar">@bar</a>,
|
|
<a class="mention" href="/u/bar">@bar</a>:
|
|
<a class="mention" href="/u/bar">@bar</a>;
|
|
<a class="mention" href="/u/bar">@bar</a>_
|
|
<a class="mention" href="/u/bar">@bar</a>-</p>
|
|
HTML
|
|
end
|
|
|
|
it "does not replace mention in cooked when mention contains a trailing underscore" do
|
|
# Older versions of Discourse detected a trailing underscore as part of a username.
|
|
# That doesn't happen anymore, so we need to do create the `cooked` for this test manually.
|
|
post =
|
|
create_post_and_change_username(raw: "@foobar @foo") do |p|
|
|
p.update_columns(
|
|
raw: p.raw.gsub("@foobar", "@foo_"),
|
|
cooked: p.cooked.gsub("@foobar", "@foo_"),
|
|
)
|
|
end
|
|
|
|
expect(post.raw).to eq("@bar_ @bar")
|
|
expect(post.cooked).to eq(
|
|
%Q(<p><span class="mention">@foo_</span> <a class="mention" href="/u/bar">@bar</a></p>),
|
|
)
|
|
end
|
|
|
|
it "does not replace mentions when there are leading alphanumeric chars" do
|
|
post = create_post_and_change_username(raw: "@foo a@foo 2@foo")
|
|
|
|
expect(post.raw).to eq("@bar a@foo 2@foo")
|
|
expect(post.cooked).to eq(
|
|
%Q(<p><a class="mention" href="/u/bar">@bar</a> a@foo 2@foo</p>),
|
|
)
|
|
end
|
|
|
|
it "does not replace username within email address" do
|
|
post = create_post_and_change_username(raw: "@foo mail@foo.com")
|
|
|
|
expect(post.raw).to eq("@bar mail@foo.com")
|
|
expect(post.cooked).to eq(
|
|
%Q(<p><a class="mention" href="/u/bar">@bar</a> <a href="mailto:mail@foo.com">mail@foo.com</a></p>),
|
|
)
|
|
end
|
|
|
|
it "does not replace username in a mention of a similar username" do
|
|
Fabricate(:user, username: "foobar")
|
|
Fabricate(:user, username: "foo-bar")
|
|
Fabricate(:user, username: "foo_bar")
|
|
Fabricate(:user, username: "foo1")
|
|
|
|
post = create_post_and_change_username(raw: "@foo @foobar @foo-bar @foo_bar @foo1")
|
|
|
|
expect(post.raw).to eq("@bar @foobar @foo-bar @foo_bar @foo1")
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<p><a class="mention" href="/u/bar">@bar</a> <a class="mention" href="/u/foobar">@foobar</a> <a class="mention" href="/u/foo-bar">@foo-bar</a> <a class="mention" href="/u/foo_bar">@foo_bar</a> <a class="mention" href="/u/foo1">@foo1</a></p>
|
|
HTML
|
|
end
|
|
|
|
it "updates the path to the user even when it links to /user instead of /u" do
|
|
post = create_post_and_change_username(raw: "Hello @foo")
|
|
post.update_column(:cooked, post.cooked.gsub("/u/foo", "/users/foo"))
|
|
|
|
expect(post.raw).to eq("Hello @bar")
|
|
expect(post.cooked).to eq(%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a></p>))
|
|
end
|
|
|
|
it "replaces mentions within revisions" do
|
|
revisions = [
|
|
{ raw: "Hello Foo" },
|
|
{ title: "new topic title" },
|
|
{ raw: "Hello @foo!" },
|
|
{ raw: "Hello @foo!!" },
|
|
]
|
|
post = create_post_and_change_username(raw: "Hello @foo", revisions: revisions)
|
|
|
|
expect(post.raw).to eq("Hello @bar!!")
|
|
expect(post.cooked).to eq(%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!!</p>))
|
|
|
|
expect(post.revisions.count).to eq(4)
|
|
|
|
expect(post.revisions[0].modifications["raw"][0]).to eq("Hello @bar")
|
|
expect(post.revisions[0].modifications["raw"][1]).to eq("Hello Foo")
|
|
expect(post.revisions[0].modifications["cooked"][0]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a></p>),
|
|
)
|
|
expect(post.revisions[0].modifications["cooked"][1]).to eq("<p>Hello Foo</p>")
|
|
|
|
expect(post.revisions[1].modifications).to include("title")
|
|
|
|
expect(post.revisions[2].modifications["raw"][0]).to eq("Hello Foo")
|
|
expect(post.revisions[2].modifications["raw"][1]).to eq("Hello @bar!")
|
|
expect(post.revisions[2].modifications["cooked"][0]).to eq("<p>Hello Foo</p>")
|
|
expect(post.revisions[2].modifications["cooked"][1]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!</p>),
|
|
)
|
|
|
|
expect(post.revisions[3].modifications["raw"][0]).to eq("Hello @bar!")
|
|
expect(post.revisions[3].modifications["raw"][1]).to eq("Hello @bar!!")
|
|
expect(post.revisions[3].modifications["cooked"][0]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!</p>),
|
|
)
|
|
expect(post.revisions[3].modifications["cooked"][1]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!!</p>),
|
|
)
|
|
end
|
|
|
|
it "replaces mentions in posts marked for deletion" do
|
|
post =
|
|
create_post_and_change_username(raw: "Hello @foo") do |p|
|
|
PostDestroyer.new(p.user, p).destroy
|
|
end
|
|
|
|
expect(post.raw).to_not include("@foo")
|
|
expect(post.cooked).to_not include("foo")
|
|
expect(post.revisions.count).to eq(1)
|
|
|
|
expect(post.revisions[0].modifications["raw"][0]).to eq("Hello @bar")
|
|
expect(post.revisions[0].modifications["cooked"][0]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a></p>),
|
|
)
|
|
end
|
|
|
|
it "works when users are mentioned with HTML" do
|
|
post =
|
|
create_post_and_change_username(
|
|
raw: '<a class="mention">@foo</a> and <a class="mention">@someuser</a>',
|
|
)
|
|
|
|
expect(post.raw).to eq('<a class="mention">@bar</a> and <a class="mention">@someuser</a>')
|
|
expect(post.cooked).to match_html(
|
|
'<p><a class="mention">@bar</a> and <a class="mention">@someuser</a></p>',
|
|
)
|
|
end
|
|
|
|
it "replaces mentions of oneself in posts" do
|
|
post = create_post_and_change_username(raw: "Hello @#{user.username}", user: user)
|
|
|
|
expect(post.raw).to eq("Hello @bar")
|
|
expect(post.cooked).to eq(%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a></p>))
|
|
end
|
|
|
|
it "replaces mentions of oneself in revisions" do
|
|
revisions = [
|
|
{ raw: "Hello Foo" },
|
|
{ title: "new topic title" },
|
|
{ raw: "Hello @#{user.username}!" },
|
|
{ raw: "Hello @#{user.username}!!" },
|
|
]
|
|
|
|
post =
|
|
create_post_and_change_username(raw: "Hello @#{user.username}", revisions: revisions)
|
|
|
|
expect(post.raw).to eq("Hello @bar!!")
|
|
expect(post.cooked).to eq(%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!!</p>))
|
|
|
|
expect(post.revisions.count).to eq(4)
|
|
|
|
expect(post.revisions[0].modifications["raw"][0]).to eq("Hello @bar")
|
|
expect(post.revisions[0].modifications["cooked"][0]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a></p>),
|
|
)
|
|
expect(post.revisions[0].modifications["cooked"][1]).to eq("<p>Hello Foo</p>")
|
|
|
|
expect(post.revisions[1].modifications).to include("title")
|
|
|
|
expect(post.revisions[2].modifications["raw"][0]).to eq("Hello Foo")
|
|
expect(post.revisions[2].modifications["raw"][1]).to eq("Hello @bar!")
|
|
expect(post.revisions[2].modifications["cooked"][0]).to eq("<p>Hello Foo</p>")
|
|
expect(post.revisions[2].modifications["cooked"][1]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!</p>),
|
|
)
|
|
|
|
expect(post.revisions[3].modifications["raw"][0]).to eq("Hello @bar!")
|
|
expect(post.revisions[3].modifications["raw"][1]).to eq("Hello @bar!!")
|
|
expect(post.revisions[3].modifications["cooked"][0]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!</p>),
|
|
)
|
|
expect(post.revisions[3].modifications["cooked"][1]).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/bar">@bar</a>!!</p>),
|
|
)
|
|
end
|
|
|
|
context "when using Unicode usernames" do
|
|
before { SiteSetting.unicode_usernames = true }
|
|
let(:user) { Fabricate(:user, username: "թռչուն") }
|
|
|
|
it "it correctly updates mentions" do
|
|
post = create_post_and_change_username(raw: "Hello @թռչուն", target_username: "птица")
|
|
|
|
expect(post.raw).to eq("Hello @птица")
|
|
expect(post.cooked).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/%D0%BF%D1%82%D0%B8%D1%86%D0%B0">@птица</a></p>),
|
|
)
|
|
end
|
|
|
|
it "does not replace mentions when there are leading alphanumeric chars" do
|
|
post =
|
|
create_post_and_change_username(
|
|
raw: "Hello @թռչուն 鳥@թռչուն 2@թռչուն ٩@թռչուն",
|
|
target_username: "птица",
|
|
)
|
|
|
|
expect(post.raw).to eq("Hello @птица 鳥@թռչուն 2@թռչուն ٩@թռչուն")
|
|
expect(post.cooked).to eq(
|
|
%Q(<p>Hello <a class="mention" href="/u/%D0%BF%D1%82%D0%B8%D1%86%D0%B0">@птица</a> 鳥@թռչուն 2@թռչուն ٩@թռչուն</p>),
|
|
)
|
|
end
|
|
|
|
it "does not replace username in a mention of a similar username" do
|
|
Fabricate(:user, username: "թռչուն鳥")
|
|
Fabricate(:user, username: "թռչուն-鳥")
|
|
Fabricate(:user, username: "թռչուն_鳥")
|
|
Fabricate(:user, username: "թռչուն٩")
|
|
|
|
post =
|
|
create_post_and_change_username(
|
|
raw: "@թռչուն @թռչուն鳥 @թռչուն-鳥 @թռչուն_鳥 @թռչուն٩",
|
|
target_username: "птица",
|
|
)
|
|
|
|
expect(post.raw).to eq("@птица @թռչուն鳥 @թռչուն-鳥 @թռչուն_鳥 @թռչուն٩")
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<p><a class="mention" href="/u/%D0%BF%D1%82%D0%B8%D1%86%D0%B0">@птица</a> <a class="mention" href="/u/%D5%A9%D5%BC%D5%B9%D5%B8%D6%82%D5%B6%E9%B3%A5">@թռչուն鳥</a> <a class="mention" href="/u/%D5%A9%D5%BC%D5%B9%D5%B8%D6%82%D5%B6-%E9%B3%A5">@թռչուն-鳥</a> <a class="mention" href="/u/%D5%A9%D5%BC%D5%B9%D5%B8%D6%82%D5%B6_%E9%B3%A5">@թռչուն_鳥</a> <a class="mention" href="/u/%D5%A9%D5%BC%D5%B9%D5%B8%D6%82%D5%B6%D9%A9">@թռչուն٩</a></p>
|
|
HTML
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when there are quotes" do
|
|
let(:quoted_post) do
|
|
create_post(user: user, topic: topic, post_number: 1, raw: "quoted post")
|
|
end
|
|
let(:avatar_url) { user.avatar_template_url.gsub("{size}", "48") }
|
|
|
|
it "replaces the username in quote tags and updates avatar" do
|
|
post = create_post_and_change_username(raw: <<~RAW)
|
|
Lorem ipsum
|
|
|
|
[quote="foo, post:1, topic:#{quoted_post.topic.id}"]
|
|
quoted post
|
|
[/quote]
|
|
|
|
[quote='foo']
|
|
quoted post
|
|
[/quote]
|
|
|
|
[quote=foo, post:1, topic:#{quoted_post.topic.id}]
|
|
quoted post
|
|
[/quote]
|
|
|
|
dolor sit amet
|
|
RAW
|
|
|
|
expect(post.raw).to eq(<<~RAW.strip)
|
|
Lorem ipsum
|
|
|
|
[quote="bar, post:1, topic:#{quoted_post.topic.id}"]
|
|
quoted post
|
|
[/quote]
|
|
|
|
[quote='bar']
|
|
quoted post
|
|
[/quote]
|
|
|
|
[quote=bar, post:1, topic:#{quoted_post.topic.id}]
|
|
quoted post
|
|
[/quote]
|
|
|
|
dolor sit amet
|
|
RAW
|
|
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<p>Lorem ipsum</p>
|
|
<aside class="quote no-group" data-username="bar" data-post="1" data-topic="#{quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt='' width="24" height="24" src="#{avatar_url}" class="avatar"> bar:</div>
|
|
<blockquote>
|
|
<p>quoted post</p>
|
|
</blockquote>
|
|
</aside>
|
|
<aside class="quote no-group" data-username="bar">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt="" width="24" height="24" src="#{avatar_url}" class="avatar"> bar:</div>
|
|
<blockquote>
|
|
<p>quoted post</p>
|
|
</blockquote>
|
|
</aside>
|
|
<aside class="quote no-group" data-username="bar" data-post="1" data-topic="#{quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt="" width="24" height="24" src="#{avatar_url}" class="avatar"> bar:</div>
|
|
<blockquote>
|
|
<p>quoted post</p>
|
|
</blockquote>
|
|
</aside>
|
|
<p>dolor sit amet</p>
|
|
HTML
|
|
end
|
|
|
|
it "replaces the username in new quote format" do
|
|
post = create_post_and_change_username(raw: <<~RAW)
|
|
Lorem ipsum
|
|
|
|
[quote="Foo Bar, post:1, topic:#{quoted_post.topic.id}, username:foo"]
|
|
quoted post
|
|
[/quote]
|
|
|
|
dolor sit amet
|
|
RAW
|
|
|
|
expect(post.raw).to eq(<<~RAW.strip)
|
|
Lorem ipsum
|
|
|
|
[quote="Foo Bar, post:1, topic:#{quoted_post.topic.id}, username:bar"]
|
|
quoted post
|
|
[/quote]
|
|
|
|
dolor sit amet
|
|
RAW
|
|
|
|
expect(post.cooked).to match_html(<<~HTML)
|
|
<p>Lorem ipsum</p>
|
|
<aside class="quote no-group" data-username="bar" data-post="1" data-topic="#{quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt="" width="24" height="24" src="//test.localhost/letter_avatar_proxy/v4/letter/b/b77776/48.png" class="avatar"> Foo Bar:</div>
|
|
<blockquote>
|
|
<p>quoted post</p>
|
|
</blockquote>
|
|
</aside>
|
|
<p>dolor sit amet</p>
|
|
HTML
|
|
end
|
|
|
|
context "when there is a simple quote" do
|
|
let(:raw) { <<~RAW }
|
|
Lorem ipsum
|
|
|
|
[quote="foo, post:1, topic:#{quoted_post.topic.id}"]
|
|
quoted
|
|
[/quote]
|
|
RAW
|
|
|
|
let(:expected_raw) { <<~RAW.strip }
|
|
Lorem ipsum
|
|
|
|
[quote="bar, post:1, topic:#{quoted_post.topic.id}"]
|
|
quoted
|
|
[/quote]
|
|
RAW
|
|
|
|
let(:expected_cooked) { <<~HTML.rstrip }
|
|
<p>Lorem ipsum</p>
|
|
<aside class="quote no-group" data-username="bar" data-post="1" data-topic="#{quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt='' width="24" height="24" src="#{avatar_url}" class="avatar"> bar:</div>
|
|
<blockquote>
|
|
<p>quoted</p>
|
|
</blockquote>
|
|
</aside>
|
|
HTML
|
|
|
|
it "replaces the username in quote tags when the post is deleted" do
|
|
post =
|
|
create_post_and_change_username(raw: raw) do |p|
|
|
PostDestroyer.new(Discourse.system_user, p).destroy
|
|
end
|
|
|
|
expect(post.raw).to eq(expected_raw)
|
|
expect(post.cooked).to match_html(expected_cooked)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when there are oneboxes" do
|
|
let(:quoted_post) do
|
|
create_post(user: user, topic: topic, post_number: 1, raw: "quoted post")
|
|
end
|
|
let(:avatar_url) { user_avatar_url(user) }
|
|
let(:evil_trout) { Fabricate(:evil_trout) }
|
|
let(:another_quoted_post) do
|
|
create_post(user: evil_trout, topic: topic, post_number: 2, raw: "evil post")
|
|
end
|
|
|
|
def protocol_relative_url(url)
|
|
url.sub(/^https?:/, "")
|
|
end
|
|
|
|
def user_avatar_url(u)
|
|
u.avatar_template_url.gsub("{size}", "48")
|
|
end
|
|
|
|
it "updates avatar for linked topics and posts" do
|
|
raw = "#{quoted_post.full_url}\n#{quoted_post.topic.url}"
|
|
post = create_post_and_change_username(raw: raw)
|
|
|
|
expect(post.raw).to eq(raw)
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<aside class="quote" data-post="#{quoted_post.post_number}" data-topic="#{quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt="" width="24" height="24" src="#{avatar_url}" class="avatar">
|
|
<a href="#{protocol_relative_url(quoted_post.full_url)}">#{quoted_post.topic.title}</a>
|
|
</div>
|
|
<blockquote>
|
|
quoted post
|
|
</blockquote>
|
|
</aside>
|
|
|
|
<aside class="quote" data-post="#{quoted_post.post_number}" data-topic="#{quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt="" width="24" height="24" src="#{avatar_url}" class="avatar">
|
|
<a href="#{protocol_relative_url(quoted_post.topic.url)}">#{quoted_post.topic.title}</a>
|
|
</div>
|
|
<blockquote>
|
|
quoted post
|
|
</blockquote>
|
|
</aside>
|
|
HTML
|
|
end
|
|
|
|
it "does not update the wrong avatar" do
|
|
raw = "#{quoted_post.full_url}\n#{another_quoted_post.full_url}"
|
|
post = create_post_and_change_username(raw: raw)
|
|
|
|
expect(post.raw).to eq(raw)
|
|
expect(post.cooked).to match_html <<~HTML
|
|
<aside class="quote" data-post="#{quoted_post.post_number}" data-topic="#{quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt="" width="24" height="24" src="#{avatar_url}" class="avatar">
|
|
<a href="#{protocol_relative_url(quoted_post.full_url)}">#{quoted_post.topic.title}</a>
|
|
</div>
|
|
<blockquote>
|
|
quoted post
|
|
</blockquote>
|
|
</aside>
|
|
<aside class="quote" data-post="#{another_quoted_post.post_number}" data-topic="#{another_quoted_post.topic.id}">
|
|
<div class="title">
|
|
<div class="quote-controls"></div>
|
|
<img alt="" width="24" height="24" src="#{user_avatar_url(evil_trout)}" class="avatar">
|
|
<a href="#{protocol_relative_url(another_quoted_post.full_url)}">#{another_quoted_post.topic.title}</a>
|
|
</div>
|
|
<blockquote>
|
|
evil post
|
|
</blockquote>
|
|
</aside>
|
|
HTML
|
|
end
|
|
end
|
|
|
|
it "updates username in small action posts" do
|
|
invited_by = Fabricate(:user)
|
|
p1 = topic.add_small_action(invited_by, "invited_user", "foo")
|
|
p2 = topic.add_small_action(invited_by, "invited_user", "foobar")
|
|
UsernameChanger.change(user, "bar")
|
|
|
|
expect(p1.reload.custom_fields["action_code_who"]).to eq("bar")
|
|
expect(p2.reload.custom_fields["action_code_who"]).to eq("foobar")
|
|
end
|
|
end
|
|
|
|
context "when there are notifications" do
|
|
def create_notification(type, notified_user, post, data = {})
|
|
Fabricate(
|
|
:notification,
|
|
notification_type: Notification.types[type],
|
|
user: notified_user,
|
|
data: data.to_json,
|
|
topic: post&.topic,
|
|
post_number: post&.post_number,
|
|
)
|
|
end
|
|
|
|
def notification_data(notification)
|
|
JSON.parse(notification.reload.data, symbolize_names: true)
|
|
end
|
|
|
|
def original_and_display_username(username)
|
|
{ original_username: username, display_username: username, foo: "bar" }
|
|
end
|
|
|
|
def original_username_and_some_text_as_display_username(username)
|
|
{ original_username: username, display_username: "some text", foo: "bar" }
|
|
end
|
|
|
|
def only_display_username(username)
|
|
{ display_username: username }
|
|
end
|
|
|
|
def username_and_something_else(username)
|
|
{ username: username, foo: "bar" }
|
|
end
|
|
|
|
it "replaces usernames in notifications" do
|
|
renamed_user = Fabricate(:user, username: "alice")
|
|
another_user = Fabricate(:user, username: "another_user")
|
|
notified_user = Fabricate(:user)
|
|
p1 = Fabricate(:post, post_number: 1, user: renamed_user)
|
|
p2 = Fabricate(:post, post_number: 1, user: another_user)
|
|
Fabricate(
|
|
:invited_user,
|
|
invite: Fabricate(:invite, invited_by: notified_user),
|
|
user: renamed_user,
|
|
)
|
|
Fabricate(
|
|
:invited_user,
|
|
invite: Fabricate(:invite, invited_by: notified_user),
|
|
user: another_user,
|
|
)
|
|
|
|
n01 =
|
|
create_notification(:mentioned, notified_user, p1, original_and_display_username("alice"))
|
|
n02 =
|
|
create_notification(
|
|
:mentioned,
|
|
notified_user,
|
|
p2,
|
|
original_and_display_username("another_user"),
|
|
)
|
|
n03 =
|
|
create_notification(
|
|
:mentioned,
|
|
notified_user,
|
|
p1,
|
|
original_username_and_some_text_as_display_username("alice"),
|
|
)
|
|
n04 = create_notification(:mentioned, notified_user, p1, only_display_username("alice"))
|
|
n05 =
|
|
create_notification(:invitee_accepted, notified_user, nil, only_display_username("alice"))
|
|
n06 =
|
|
create_notification(
|
|
:invitee_accepted,
|
|
notified_user,
|
|
nil,
|
|
only_display_username("another_user"),
|
|
)
|
|
n07 =
|
|
create_notification(
|
|
:granted_badge,
|
|
renamed_user,
|
|
nil,
|
|
username_and_something_else("alice"),
|
|
)
|
|
n08 =
|
|
create_notification(
|
|
:granted_badge,
|
|
another_user,
|
|
nil,
|
|
username_and_something_else("another_user"),
|
|
)
|
|
n09 =
|
|
create_notification(
|
|
:group_message_summary,
|
|
renamed_user,
|
|
nil,
|
|
username_and_something_else("alice"),
|
|
)
|
|
n10 =
|
|
create_notification(
|
|
:group_message_summary,
|
|
another_user,
|
|
nil,
|
|
username_and_something_else("another_user"),
|
|
)
|
|
|
|
UsernameChanger.change(renamed_user, "bob")
|
|
|
|
expect(notification_data(n01)).to eq(original_and_display_username("bob"))
|
|
expect(notification_data(n02)).to eq(original_and_display_username("another_user"))
|
|
expect(notification_data(n03)).to eq(
|
|
original_username_and_some_text_as_display_username("bob"),
|
|
)
|
|
expect(notification_data(n04)).to eq(only_display_username("bob"))
|
|
expect(notification_data(n05)).to eq(only_display_username("bob"))
|
|
expect(notification_data(n06)).to eq(only_display_username("another_user"))
|
|
expect(notification_data(n07)).to eq(username_and_something_else("bob"))
|
|
expect(notification_data(n08)).to eq(username_and_something_else("another_user"))
|
|
expect(notification_data(n09)).to eq(username_and_something_else("bob"))
|
|
expect(notification_data(n10)).to eq(username_and_something_else("another_user"))
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#override" do
|
|
common_test_cases = [
|
|
["overrides the username if a new name is different", "john", "bill", "bill", false],
|
|
["does not change the username if a new name is the same", "john", "john", "john", false],
|
|
["overrides the username if a new name has different case", "john", "JoHN", "JoHN", false],
|
|
]
|
|
|
|
context "when unicode_usernames is off" do
|
|
before { SiteSetting.unicode_usernames = false }
|
|
|
|
[
|
|
*common_test_cases,
|
|
[
|
|
"does not change the username if a new name after unicode normalization is the same",
|
|
"john",
|
|
"john¥¥",
|
|
"john",
|
|
],
|
|
].each do |testcase_name, current, new, overrode|
|
|
it "#{testcase_name}" do
|
|
user = Fabricate(:user, username: current)
|
|
UsernameChanger.override(user, new)
|
|
expect(user.username).to eq(overrode)
|
|
end
|
|
end
|
|
|
|
it "overrides the username with username suggestions in case the username is already taken" do
|
|
user = Fabricate(:user, username: "bill")
|
|
Fabricate(:user, username: "john")
|
|
|
|
UsernameChanger.override(user, "john")
|
|
|
|
expect(user.username).to eq("john1")
|
|
end
|
|
end
|
|
|
|
context "when unicode_usernames is on" do
|
|
before { SiteSetting.unicode_usernames = true }
|
|
|
|
[
|
|
*common_test_cases,
|
|
[
|
|
"overrides the username if a new name after unicode normalization is different only in case",
|
|
"lo\u0308we",
|
|
"L\u00F6wee",
|
|
"L\u00F6wee",
|
|
],
|
|
].each do |testcase_name, current, new, overrode|
|
|
it "#{testcase_name}" do
|
|
user = Fabricate(:user, username: current)
|
|
UsernameChanger.override(user, new)
|
|
expect(user.username).to eq(overrode)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|