DEV: Apply syntax_tree formatting to `spec/*`
This commit is contained in:
parent
0cf6421716
commit
cb932d6ee1
1
.streerc
1
.streerc
|
@ -3,4 +3,3 @@
|
||||||
--ignore-files=app/*
|
--ignore-files=app/*
|
||||||
--ignore-files=db/*
|
--ignore-files=db/*
|
||||||
--ignore-files=lib/*
|
--ignore-files=lib/*
|
||||||
--ignore-files=spec/*
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:allowed_pm_user) do
|
Fabricator(:allowed_pm_user) { user }
|
||||||
user
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:api_key) do
|
Fabricator(:api_key) {}
|
||||||
|
|
||||||
end
|
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
Fabricator(:associated_group) do
|
Fabricator(:associated_group) do
|
||||||
name { sequence(:name) { |n| "group_#{n}" } }
|
name { sequence(:name) { |n| "group_#{n}" } }
|
||||||
provider_name 'google'
|
provider_name "google"
|
||||||
provider_id { SecureRandom.hex(20) }
|
provider_id { SecureRandom.hex(20) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:badge_type) do
|
Fabricator(:badge_type) { name { sequence(:name) { |i| "Silver #{i}" } } }
|
||||||
name { sequence(:name) { |i| "Silver #{i}" } }
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:badge) do
|
Fabricator(:badge) do
|
||||||
name { sequence(:name) { |i| "Badge #{i}" } }
|
name { sequence(:name) { |i| "Badge #{i}" } }
|
||||||
|
|
|
@ -10,13 +10,14 @@ end
|
||||||
|
|
||||||
Fabricator(:bookmark_next_business_day_reminder, from: :bookmark) do
|
Fabricator(:bookmark_next_business_day_reminder, from: :bookmark) do
|
||||||
reminder_at do
|
reminder_at do
|
||||||
date = if Time.zone.now.friday?
|
date =
|
||||||
Time.zone.now + 3.days
|
if Time.zone.now.friday?
|
||||||
elsif Time.zone.now.saturday?
|
Time.zone.now + 3.days
|
||||||
Time.zone.now + 2.days
|
elsif Time.zone.now.saturday?
|
||||||
else
|
Time.zone.now + 2.days
|
||||||
Time.zone.now + 1.day
|
else
|
||||||
end
|
Time.zone.now + 1.day
|
||||||
|
end
|
||||||
date.iso8601
|
date.iso8601
|
||||||
end
|
end
|
||||||
reminder_set_at { Time.zone.now }
|
reminder_set_at { Time.zone.now }
|
||||||
|
|
|
@ -6,9 +6,7 @@ Fabricator(:category) do
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:category_with_definition, from: :category) do
|
Fabricator(:category_with_definition, from: :category) { skip_category_definition false }
|
||||||
skip_category_definition false
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:private_category, from: :category) do
|
Fabricator(:private_category, from: :category) do
|
||||||
transient :group
|
transient :group
|
||||||
|
@ -20,7 +18,10 @@ Fabricator(:private_category, from: :category) do
|
||||||
|
|
||||||
after_build do |cat, transients|
|
after_build do |cat, transients|
|
||||||
cat.update!(read_restricted: true)
|
cat.update!(read_restricted: true)
|
||||||
cat.category_groups.build(group_id: transients[:group].id, permission_type: transients[:permission_type] || CategoryGroup.permission_types[:full])
|
cat.category_groups.build(
|
||||||
|
group_id: transients[:group].id,
|
||||||
|
permission_type: transients[:permission_type] || CategoryGroup.permission_types[:full],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ Fabricator(:link_category, from: :category) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:mailinglist_mirror_category, from: :category) do
|
Fabricator(:mailinglist_mirror_category, from: :category) do
|
||||||
email_in 'list@example.com'
|
email_in "list@example.com"
|
||||||
email_in_allow_strangers true
|
email_in_allow_strangers true
|
||||||
mailinglist_mirror true
|
mailinglist_mirror true
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
|
|
||||||
Fabricator(:color_scheme) do
|
Fabricator(:color_scheme) do
|
||||||
name { sequence(:name) { |i| "Palette #{i}" } }
|
name { sequence(:name) { |i| "Palette #{i}" } }
|
||||||
color_scheme_colors(count: 2) { |attrs, i| Fabricate.build(:color_scheme_color, color_scheme: nil) }
|
color_scheme_colors(count: 2) do |attrs, i|
|
||||||
|
Fabricate.build(:color_scheme_color, color_scheme: nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,12 @@ Fabricator(:external_upload_stub) do
|
||||||
|
|
||||||
created_by { Fabricate(:user) }
|
created_by { Fabricate(:user) }
|
||||||
original_filename "test.txt"
|
original_filename "test.txt"
|
||||||
key { |attrs| FileStore::BaseStore.temporary_upload_path("test.txt", folder_prefix: attrs[:folder_prefix] || "") }
|
key do |attrs|
|
||||||
|
FileStore::BaseStore.temporary_upload_path(
|
||||||
|
"test.txt",
|
||||||
|
folder_prefix: attrs[:folder_prefix] || "",
|
||||||
|
)
|
||||||
|
end
|
||||||
upload_type "card_background"
|
upload_type "card_background"
|
||||||
filesize 1024
|
filesize 1024
|
||||||
status 1
|
status 1
|
||||||
|
@ -14,16 +19,28 @@ end
|
||||||
Fabricator(:image_external_upload_stub, from: :external_upload_stub) do
|
Fabricator(:image_external_upload_stub, from: :external_upload_stub) do
|
||||||
original_filename "logo.png"
|
original_filename "logo.png"
|
||||||
filesize 1024
|
filesize 1024
|
||||||
key { |attrs| FileStore::BaseStore.temporary_upload_path("logo.png", folder_prefix: attrs[:folder_prefix] || "") }
|
key do |attrs|
|
||||||
|
FileStore::BaseStore.temporary_upload_path(
|
||||||
|
"logo.png",
|
||||||
|
folder_prefix: attrs[:folder_prefix] || "",
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:attachment_external_upload_stub, from: :external_upload_stub) do
|
Fabricator(:attachment_external_upload_stub, from: :external_upload_stub) do
|
||||||
original_filename "file.pdf"
|
original_filename "file.pdf"
|
||||||
filesize 1024
|
filesize 1024
|
||||||
key { |attrs| FileStore::BaseStore.temporary_upload_path("file.pdf", folder_prefix: attrs[:folder_prefix] || "") }
|
key do |attrs|
|
||||||
|
FileStore::BaseStore.temporary_upload_path(
|
||||||
|
"file.pdf",
|
||||||
|
folder_prefix: attrs[:folder_prefix] || "",
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:multipart_external_upload_stub, from: :external_upload_stub) do
|
Fabricator(:multipart_external_upload_stub, from: :external_upload_stub) do
|
||||||
multipart true
|
multipart true
|
||||||
external_upload_identifier { "#{SecureRandom.hex(6)}._#{SecureRandom.hex(6)}_#{SecureRandom.hex(6)}.d.ghQ" }
|
external_upload_identifier do
|
||||||
|
"#{SecureRandom.hex(6)}._#{SecureRandom.hex(6)}_#{SecureRandom.hex(6)}.d.ghQ"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:group) do
|
Fabricator(:group) { name { sequence(:name) { |n| "my_group_#{n}" } } }
|
||||||
name { sequence(:name) { |n| "my_group_#{n}" } }
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:public_group, from: :group) do
|
Fabricator(:public_group, from: :group) do
|
||||||
public_admission true
|
public_admission true
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
Fabricator(:invite) do
|
Fabricator(:invite) do
|
||||||
invited_by(fabricator: :user)
|
invited_by(fabricator: :user)
|
||||||
email 'iceking@ADVENTURETIME.ooo'
|
email "iceking@ADVENTURETIME.ooo"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:muted_user) do
|
Fabricator(:muted_user) { user }
|
||||||
user
|
|
||||||
end
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ Fabricator(:private_message_notification, from: :notification) do
|
||||||
original_post_type: post.post_type,
|
original_post_type: post.post_type,
|
||||||
original_username: post.user.username,
|
original_username: post.user.username,
|
||||||
revision_number: nil,
|
revision_number: nil,
|
||||||
display_username: post.user.username
|
display_username: post.user.username,
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -44,7 +44,7 @@ Fabricator(:bookmark_reminder_notification, from: :notification) do
|
||||||
original_username: post.user.username,
|
original_username: post.user.username,
|
||||||
revision_number: nil,
|
revision_number: nil,
|
||||||
display_username: post.user.username,
|
display_username: post.user.username,
|
||||||
bookmark_name: "Check out Mr Freeze's opinion here"
|
bookmark_name: "Check out Mr Freeze's opinion here",
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -58,7 +58,7 @@ Fabricator(:replied_notification, from: :notification) do
|
||||||
original_post_id: post.id,
|
original_post_id: post.id,
|
||||||
original_username: post.user.username,
|
original_username: post.user.username,
|
||||||
revision_number: nil,
|
revision_number: nil,
|
||||||
display_username: post.user.username
|
display_username: post.user.username,
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -73,7 +73,7 @@ Fabricator(:posted_notification, from: :notification) do
|
||||||
original_post_type: post.post_type,
|
original_post_type: post.post_type,
|
||||||
original_username: post.user.username,
|
original_username: post.user.username,
|
||||||
revision_number: nil,
|
revision_number: nil,
|
||||||
display_username: post.user.username
|
display_username: post.user.username,
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -87,7 +87,7 @@ Fabricator(:mentioned_notification, from: :notification) do
|
||||||
original_post_type: attrs[:post].post_type,
|
original_post_type: attrs[:post].post_type,
|
||||||
original_username: attrs[:post].user.username,
|
original_username: attrs[:post].user.username,
|
||||||
revision_number: nil,
|
revision_number: nil,
|
||||||
display_username: attrs[:post].user.username
|
display_username: attrs[:post].user.username,
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -101,7 +101,7 @@ Fabricator(:watching_first_post_notification, from: :notification) do
|
||||||
original_post_type: attrs[:post].post_type,
|
original_post_type: attrs[:post].post_type,
|
||||||
original_username: attrs[:post].user.username,
|
original_username: attrs[:post].user.username,
|
||||||
revision_number: nil,
|
revision_number: nil,
|
||||||
display_username: attrs[:post].user.username
|
display_username: attrs[:post].user.username,
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:permalink) do
|
Fabricator(:permalink) { url { sequence(:url) { |i| "my/#{i}/url" } } }
|
||||||
url { sequence(:url) { |i| "my/#{i}/url" } }
|
|
||||||
end
|
|
||||||
|
|
|
@ -7,19 +7,17 @@ Fabricator(:post) do
|
||||||
post_type Post.types[:regular]
|
post_type Post.types[:regular]
|
||||||
|
|
||||||
# Fabrication bypasses PostCreator, for performance reasons, where the counts are updated so we have to handle this manually here.
|
# Fabrication bypasses PostCreator, for performance reasons, where the counts are updated so we have to handle this manually here.
|
||||||
after_create do |post, _transients|
|
after_create { |post, _transients| UserStatCountUpdater.increment!(post) }
|
||||||
UserStatCountUpdater.increment!(post)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:post_with_long_raw_content, from: :post) do
|
Fabricator(:post_with_long_raw_content, from: :post) do
|
||||||
raw 'This is a sample post with semi-long raw content. The raw content is also more than
|
raw "This is a sample post with semi-long raw content. The raw content is also more than
|
||||||
two hundred characters to satisfy any test conditions that require content longer
|
two hundred characters to satisfy any test conditions that require content longer
|
||||||
than the typical test post raw content. It really is some long content, folks.'
|
than the typical test post raw content. It really is some long content, folks."
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:post_with_youtube, from: :post) do
|
Fabricator(:post_with_youtube, from: :post) do
|
||||||
raw 'http://www.youtube.com/watch?v=9bZkp7q19f0'
|
raw "http://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||||
cooked '<p><a href="http://www.youtube.com/watch?v=9bZkp7q19f0" class="onebox" target="_blank">http://www.youtube.com/watch?v=9bZkp7q19f0</a></p>'
|
cooked '<p><a href="http://www.youtube.com/watch?v=9bZkp7q19f0" class="onebox" target="_blank">http://www.youtube.com/watch?v=9bZkp7q19f0</a></p>'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -39,7 +37,7 @@ Fabricator(:basic_reply, from: :post) do
|
||||||
user(fabricator: :coding_horror)
|
user(fabricator: :coding_horror)
|
||||||
reply_to_post_number 1
|
reply_to_post_number 1
|
||||||
topic
|
topic
|
||||||
raw 'this reply has no quotes'
|
raw "this reply has no quotes"
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:reply, from: :post) do
|
Fabricator(:reply, from: :post) do
|
||||||
|
@ -51,14 +49,12 @@ Fabricator(:reply, from: :post) do
|
||||||
'
|
'
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:post_with_plenty_of_images, from: :post) do
|
Fabricator(:post_with_plenty_of_images, from: :post) { cooked <<~HTML }
|
||||||
cooked <<~HTML
|
|
||||||
<aside class="quote"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></aside>
|
<aside class="quote"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></aside>
|
||||||
<div class="onebox-result"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></div>
|
<div class="onebox-result"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></div>
|
||||||
<div class="onebox"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></div>
|
<div class="onebox"><img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"></div>
|
||||||
<p>With an emoji! <img src="//cdn.discourse.org/meta/images/emoji/twitter/smile.png?v=#{Emoji::EMOJI_VERSION}" title=":smile:" class="emoji" alt="smile" loading="lazy" width="20" height="20"></p>
|
<p>With an emoji! <img src="//cdn.discourse.org/meta/images/emoji/twitter/smile.png?v=#{Emoji::EMOJI_VERSION}" title=":smile:" class="emoji" alt="smile" loading="lazy" width="20" height="20"></p>
|
||||||
HTML
|
HTML
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:post_with_uploaded_image, from: :post) do
|
Fabricator(:post_with_uploaded_image, from: :post) do
|
||||||
raw { "<img src=\"#{Fabricate(:image_upload)}\" width=\"1500\" height=\"2000\">" }
|
raw { "<img src=\"#{Fabricate(:image_upload)}\" width=\"1500\" height=\"2000\">" }
|
||||||
|
@ -101,8 +97,7 @@ Fabricator(:post_with_uploads, from: :post) do
|
||||||
"
|
"
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:post_with_uploads_and_links, from: :post) do
|
Fabricator(:post_with_uploads_and_links, from: :post) { raw <<~MD }
|
||||||
raw <<~MD
|
|
||||||
<a href="/#{Discourse.store.upload_path}/original/2X/2345678901234567.jpg">Link</a>
|
<a href="/#{Discourse.store.upload_path}/original/2X/2345678901234567.jpg">Link</a>
|
||||||
<img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg">
|
<img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg">
|
||||||
<a href="http://www.google.com">Google</a>
|
<a href="http://www.google.com">Google</a>
|
||||||
|
@ -110,7 +105,6 @@ Fabricator(:post_with_uploads_and_links, from: :post) do
|
||||||
<a class="attachment" href="/#{Discourse.store.upload_path}/original/1X/af2c2618032c679333bebf745e75f9088748d737.txt">text.txt</a> (20 Bytes)
|
<a class="attachment" href="/#{Discourse.store.upload_path}/original/1X/af2c2618032c679333bebf745e75f9088748d737.txt">text.txt</a> (20 Bytes)
|
||||||
:smile:
|
:smile:
|
||||||
MD
|
MD
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:post_with_external_links, from: :post) do
|
Fabricator(:post_with_external_links, from: :post) do
|
||||||
user
|
user
|
||||||
|
@ -130,14 +124,15 @@ Fabricator(:private_message_post, from: :post) do
|
||||||
transient :recipient
|
transient :recipient
|
||||||
user
|
user
|
||||||
topic do |attrs|
|
topic do |attrs|
|
||||||
Fabricate(:private_message_topic,
|
Fabricate(
|
||||||
|
:private_message_topic,
|
||||||
user: attrs[:user],
|
user: attrs[:user],
|
||||||
created_at: attrs[:created_at],
|
created_at: attrs[:created_at],
|
||||||
subtype: TopicSubtype.user_to_user,
|
subtype: TopicSubtype.user_to_user,
|
||||||
topic_allowed_users: [
|
topic_allowed_users: [
|
||||||
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
|
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
|
||||||
Fabricate.build(:topic_allowed_user, user: attrs[:recipient] || Fabricate(:user))
|
Fabricate.build(:topic_allowed_user, user: attrs[:recipient] || Fabricate(:user)),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
raw "Ssshh! This is our secret conversation!"
|
raw "Ssshh! This is our secret conversation!"
|
||||||
|
@ -147,16 +142,15 @@ Fabricator(:group_private_message_post, from: :post) do
|
||||||
transient :recipients
|
transient :recipients
|
||||||
user
|
user
|
||||||
topic do |attrs|
|
topic do |attrs|
|
||||||
Fabricate(:private_message_topic,
|
Fabricate(
|
||||||
|
:private_message_topic,
|
||||||
user: attrs[:user],
|
user: attrs[:user],
|
||||||
created_at: attrs[:created_at],
|
created_at: attrs[:created_at],
|
||||||
subtype: TopicSubtype.user_to_user,
|
subtype: TopicSubtype.user_to_user,
|
||||||
topic_allowed_users: [
|
topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])],
|
||||||
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
|
|
||||||
],
|
|
||||||
topic_allowed_groups: [
|
topic_allowed_groups: [
|
||||||
Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group))
|
Fabricate.build(:topic_allowed_group, group: attrs[:recipients] || Fabricate(:group)),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
raw "Ssshh! This is our group secret conversation!"
|
raw "Ssshh! This is our group secret conversation!"
|
||||||
|
@ -165,13 +159,12 @@ end
|
||||||
Fabricator(:private_message_post_one_user, from: :post) do
|
Fabricator(:private_message_post_one_user, from: :post) do
|
||||||
user
|
user
|
||||||
topic do |attrs|
|
topic do |attrs|
|
||||||
Fabricate(:private_message_topic,
|
Fabricate(
|
||||||
|
:private_message_topic,
|
||||||
user: attrs[:user],
|
user: attrs[:user],
|
||||||
created_at: attrs[:created_at],
|
created_at: attrs[:created_at],
|
||||||
subtype: TopicSubtype.user_to_user,
|
subtype: TopicSubtype.user_to_user,
|
||||||
topic_allowed_users: [
|
topic_allowed_users: [Fabricate.build(:topic_allowed_user, user: attrs[:user])],
|
||||||
Fabricate.build(:topic_allowed_user, user: attrs[:user]),
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
raw "Ssshh! This is our secret conversation!"
|
raw "Ssshh! This is our secret conversation!"
|
||||||
|
@ -188,10 +181,6 @@ Fabricator(:post_via_email, from: :post) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:whisper, from: :post) do
|
Fabricator(:whisper, from: :post) { post_type Post.types[:whisper] }
|
||||||
post_type Post.types[:whisper]
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:small_action, from: :post) do
|
Fabricator(:small_action, from: :post) { post_type Post.types[:small_action] }
|
||||||
post_type Post.types[:small_action]
|
|
||||||
end
|
|
||||||
|
|
|
@ -4,7 +4,5 @@ Fabricator(:post_revision) do
|
||||||
post
|
post
|
||||||
user
|
user
|
||||||
number 2
|
number 2
|
||||||
modifications do
|
modifications { { "cooked" => %w[<p>BEFORE</p> <p>AFTER</p>], "raw" => %w[BEFORE AFTER] } }
|
||||||
{ "cooked" => ["<p>BEFORE</p>", "<p>AFTER</p>"], "raw" => ["BEFORE", "AFTER"] }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,70 +2,75 @@
|
||||||
|
|
||||||
Fabricator(:reviewable) do
|
Fabricator(:reviewable) do
|
||||||
reviewable_by_moderator true
|
reviewable_by_moderator true
|
||||||
type 'ReviewableUser'
|
type "ReviewableUser"
|
||||||
created_by { Fabricate(:user) }
|
created_by { Fabricate(:user) }
|
||||||
target_id { Fabricate(:user).id }
|
target_id { Fabricate(:user).id }
|
||||||
target_type "User"
|
target_type "User"
|
||||||
target_created_by { Fabricate(:user) }
|
target_created_by { Fabricate(:user) }
|
||||||
category
|
category
|
||||||
score 1.23
|
score 1.23
|
||||||
payload {
|
payload { { list: [1, 2, 3], name: "bandersnatch" } }
|
||||||
{ list: [1, 2, 3], name: 'bandersnatch' }
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:reviewable_queued_post_topic, class_name: :reviewable_queued_post) do
|
Fabricator(:reviewable_queued_post_topic, class_name: :reviewable_queued_post) do
|
||||||
reviewable_by_moderator true
|
reviewable_by_moderator true
|
||||||
type 'ReviewableQueuedPost'
|
type "ReviewableQueuedPost"
|
||||||
created_by { Fabricate(:user) }
|
created_by { Fabricate(:user) }
|
||||||
category
|
category
|
||||||
payload {
|
payload do
|
||||||
{
|
{
|
||||||
raw: "hello world post contents.",
|
raw: "hello world post contents.",
|
||||||
title: "queued post title",
|
title: "queued post title",
|
||||||
tags: ['cool', 'neat'],
|
tags: %w[cool neat],
|
||||||
extra: "some extra data",
|
extra: "some extra data",
|
||||||
archetype: 'regular'
|
archetype: "regular",
|
||||||
}
|
}
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:reviewable_queued_post) do
|
Fabricator(:reviewable_queued_post) do
|
||||||
reviewable_by_moderator true
|
reviewable_by_moderator true
|
||||||
type 'ReviewableQueuedPost'
|
type "ReviewableQueuedPost"
|
||||||
created_by { Fabricate(:user) }
|
created_by { Fabricate(:user) }
|
||||||
topic
|
topic
|
||||||
payload {
|
payload do
|
||||||
{
|
{
|
||||||
raw: "hello world post contents.",
|
raw: "hello world post contents.",
|
||||||
reply_to_post_number: 1,
|
reply_to_post_number: 1,
|
||||||
via_email: true,
|
via_email: true,
|
||||||
raw_email: 'store_me',
|
raw_email: "store_me",
|
||||||
auto_track: true,
|
auto_track: true,
|
||||||
custom_fields: { hello: 'world' },
|
custom_fields: {
|
||||||
cooking_options: { cat: 'hat' },
|
hello: "world",
|
||||||
|
},
|
||||||
|
cooking_options: {
|
||||||
|
cat: "hat",
|
||||||
|
},
|
||||||
cook_method: Post.cook_methods[:raw_html],
|
cook_method: Post.cook_methods[:raw_html],
|
||||||
image_sizes: { "http://foo.bar/image.png" => { "width" => 0, "height" => 222 } }
|
image_sizes: {
|
||||||
|
"http://foo.bar/image.png" => {
|
||||||
|
"width" => 0,
|
||||||
|
"height" => 222,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:reviewable_flagged_post) do
|
Fabricator(:reviewable_flagged_post) do
|
||||||
reviewable_by_moderator true
|
reviewable_by_moderator true
|
||||||
type 'ReviewableFlaggedPost'
|
type "ReviewableFlaggedPost"
|
||||||
created_by { Fabricate(:user) }
|
created_by { Fabricate(:user) }
|
||||||
topic
|
topic
|
||||||
target_type 'Post'
|
target_type "Post"
|
||||||
target { Fabricate(:post) }
|
target { Fabricate(:post) }
|
||||||
reviewable_scores { |p| [
|
reviewable_scores { |p| [Fabricate.build(:reviewable_score, reviewable_id: p[:id])] }
|
||||||
Fabricate.build(:reviewable_score, reviewable_id: p[:id]),
|
|
||||||
]}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:reviewable_user) do
|
Fabricator(:reviewable_user) do
|
||||||
reviewable_by_moderator true
|
reviewable_by_moderator true
|
||||||
type 'ReviewableUser'
|
type "ReviewableUser"
|
||||||
created_by { Fabricate(:user) }
|
created_by { Fabricate(:user) }
|
||||||
target_type 'User'
|
target_type "User"
|
||||||
target { Fabricate(:user) }
|
target { Fabricate(:user) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:screened_url) do
|
Fabricator(:screened_url) do
|
||||||
url { sequence(:url) { |n| "spammers#{n}.org/buy/stuff" } }
|
url { sequence(:url) { |n| "spammers#{n}.org/buy/stuff" } }
|
||||||
domain { sequence(:domain) { |n| "spammers#{n}.org" } }
|
domain { sequence(:domain) { |n| "spammers#{n}.org" } }
|
||||||
action_type ScreenedEmail.actions[:do_nothing]
|
action_type ScreenedEmail.actions[:do_nothing]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:sidebar_section_link) do
|
Fabricator(:sidebar_section_link) { user }
|
||||||
user
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:category_sidebar_section_link, from: :sidebar_section_link) do
|
Fabricator(:category_sidebar_section_link, from: :sidebar_section_link) do
|
||||||
linkable(fabricator: :category)
|
linkable(fabricator: :category)
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:tag_sidebar_section_link, from: :sidebar_section_link) do
|
Fabricator(:tag_sidebar_section_link, from: :sidebar_section_link) { linkable(fabricator: :tag) }
|
||||||
linkable(fabricator: :tag)
|
|
||||||
end
|
|
||||||
|
|
|
@ -5,5 +5,7 @@ Fabricator(:single_sign_on_record) do
|
||||||
external_id { sequence(:external_id) { |i| "ext_#{i}" } }
|
external_id { sequence(:external_id) { |i| "ext_#{i}" } }
|
||||||
external_username { sequence(:username) { |i| "bruce#{i}" } }
|
external_username { sequence(:username) { |i| "bruce#{i}" } }
|
||||||
external_email { sequence(:email) { |i| "bruce#{i}@wayne.com" } }
|
external_email { sequence(:email) { |i| "bruce#{i}@wayne.com" } }
|
||||||
last_payload { sequence(:last_payload) { |i| "nonce=#{i}1870a940bbcbb46f06880ed338d58a07&name=" } }
|
last_payload do
|
||||||
|
sequence(:last_payload) { |i| "nonce=#{i}1870a940bbcbb46f06880ed338d58a07&name=" }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:tag) do
|
Fabricator(:tag) { name { sequence(:name) { |i| "tag#{i + 1}" } } }
|
||||||
name { sequence(:name) { |i| "tag#{i + 1}" } }
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:tag_group) do
|
Fabricator(:tag_group) { name { sequence(:name) { |i| "tag_group_#{i}" } } }
|
||||||
name { sequence(:name) { |i| "tag_group_#{i}" } }
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:topic_allowed_user) do
|
Fabricator(:topic_allowed_user) { user }
|
||||||
user
|
|
||||||
end
|
|
||||||
|
|
|
@ -8,25 +8,21 @@ Fabricator(:topic) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:deleted_topic, from: :topic) do
|
Fabricator(:deleted_topic, from: :topic) { deleted_at { 1.minute.ago } }
|
||||||
deleted_at { 1.minute.ago }
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:closed_topic, from: :topic) do
|
Fabricator(:closed_topic, from: :topic) { closed true }
|
||||||
closed true
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:banner_topic, from: :topic) do
|
Fabricator(:banner_topic, from: :topic) { archetype Archetype.banner }
|
||||||
archetype Archetype.banner
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:private_message_topic, from: :topic) do
|
Fabricator(:private_message_topic, from: :topic) do
|
||||||
transient :recipient
|
transient :recipient
|
||||||
category_id { nil }
|
category_id { nil }
|
||||||
title { sequence(:title) { |i| "This is a private message #{i}" } }
|
title { sequence(:title) { |i| "This is a private message #{i}" } }
|
||||||
archetype "private_message"
|
archetype "private_message"
|
||||||
topic_allowed_users { |t| [
|
topic_allowed_users do |t|
|
||||||
Fabricate.build(:topic_allowed_user, user: t[:user]),
|
[
|
||||||
Fabricate.build(:topic_allowed_user, user: t[:recipient] || Fabricate(:user))
|
Fabricate.build(:topic_allowed_user, user: t[:user]),
|
||||||
]}
|
Fabricate.build(:topic_allowed_user, user: t[:recipient] || Fabricate(:user)),
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,9 +12,7 @@ Fabricator(:upload) do
|
||||||
|
|
||||||
url do |attrs|
|
url do |attrs|
|
||||||
sequence(:url) do |n|
|
sequence(:url) do |n|
|
||||||
Discourse.store.get_path_for(
|
Discourse.store.get_path_for("original", n + 1, attrs[:sha1], ".#{attrs[:extension]}")
|
||||||
"original", n + 1, attrs[:sha1], ".#{attrs[:extension]}"
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,15 +33,16 @@ Fabricator(:image_upload, from: :upload) do
|
||||||
transient color: "white"
|
transient color: "white"
|
||||||
|
|
||||||
after_create do |upload, transients|
|
after_create do |upload, transients|
|
||||||
file = Tempfile.new(['fabricated', '.png'])
|
file = Tempfile.new(%w[fabricated .png])
|
||||||
`convert -size #{upload.width}x#{upload.height} xc:#{transients[:color]} "#{file.path}"`
|
`convert -size #{upload.width}x#{upload.height} xc:#{transients[:color]} "#{file.path}"`
|
||||||
|
|
||||||
upload.url = Discourse.store.store_upload(file, upload)
|
upload.url = Discourse.store.store_upload(file, upload)
|
||||||
upload.sha1 = Upload.generate_digest(file.path)
|
upload.sha1 = Upload.generate_digest(file.path)
|
||||||
|
|
||||||
WebMock
|
WebMock.stub_request(:get, "http://#{Discourse.current_hostname}#{upload.url}").to_return(
|
||||||
.stub_request(:get, "http://#{Discourse.current_hostname}#{upload.url}")
|
status: 200,
|
||||||
.to_return(status: 200, body: File.new(file.path))
|
body: File.new(file.path),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,13 +71,9 @@ end
|
||||||
Fabricator(:upload_s3, from: :upload) do
|
Fabricator(:upload_s3, from: :upload) do
|
||||||
url do |attrs|
|
url do |attrs|
|
||||||
sequence(:url) do |n|
|
sequence(:url) do |n|
|
||||||
path = +Discourse.store.get_path_for(
|
path = +Discourse.store.get_path_for("original", n + 1, attrs[:sha1], ".#{attrs[:extension]}")
|
||||||
"original", n + 1, attrs[:sha1], ".#{attrs[:extension]}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if Rails.configuration.multisite
|
path.prepend(File.join(Discourse.store.upload_path, "/")) if Rails.configuration.multisite
|
||||||
path.prepend(File.join(Discourse.store.upload_path, "/"))
|
|
||||||
end
|
|
||||||
|
|
||||||
File.join(Discourse.store.absolute_base_url, path)
|
File.join(Discourse.store.absolute_base_url, path)
|
||||||
end
|
end
|
||||||
|
@ -87,15 +82,13 @@ end
|
||||||
|
|
||||||
Fabricator(:s3_image_upload, from: :upload_s3) do
|
Fabricator(:s3_image_upload, from: :upload_s3) do
|
||||||
after_create do |upload|
|
after_create do |upload|
|
||||||
file = Tempfile.new(['fabricated', '.png'])
|
file = Tempfile.new(%w[fabricated .png])
|
||||||
`convert -size #{upload.width}x#{upload.height} xc:white "#{file.path}"`
|
`convert -size #{upload.width}x#{upload.height} xc:white "#{file.path}"`
|
||||||
|
|
||||||
upload.url = Discourse.store.store_upload(file, upload)
|
upload.url = Discourse.store.store_upload(file, upload)
|
||||||
upload.sha1 = Upload.generate_digest(file.path)
|
upload.sha1 = Upload.generate_digest(file.path)
|
||||||
|
|
||||||
WebMock
|
WebMock.stub_request(:get, upload.url).to_return(status: 200, body: File.new(file.path))
|
||||||
.stub_request(:get, upload.url)
|
|
||||||
.to_return(status: 200, body: File.new(file.path))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
Fabricator(:user_api_key) do
|
Fabricator(:user_api_key) do
|
||||||
user
|
user
|
||||||
client_id { SecureRandom.hex }
|
client_id { SecureRandom.hex }
|
||||||
application_name 'some app'
|
application_name "some app"
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:user_api_key_scope)
|
Fabricator(:user_api_key_scope)
|
||||||
|
|
||||||
Fabricator(:readonly_user_api_key, from: :user_api_key) do
|
Fabricator(:readonly_user_api_key, from: :user_api_key) do
|
||||||
scopes { [Fabricate.build(:user_api_key_scope, name: 'read')] }
|
scopes { [Fabricate.build(:user_api_key_scope, name: "read")] }
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:bookmarks_calendar_user_api_key, from: :user_api_key) do
|
Fabricator(:bookmarks_calendar_user_api_key, from: :user_api_key) do
|
||||||
scopes { [Fabricate.build(:user_api_key_scope, name: 'bookmarks_calendar')] }
|
scopes { [Fabricate.build(:user_api_key_scope, name: "bookmarks_calendar")] }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:user_avatar) do
|
Fabricator(:user_avatar) { user }
|
||||||
user
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:user_stat) do
|
Fabricator(:user_stat) {}
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:user, class_name: :user) do
|
Fabricator(:user, class_name: :user) do
|
||||||
name 'Bruce Wayne'
|
name "Bruce Wayne"
|
||||||
username { sequence(:username) { |i| "bruce#{i}" } }
|
username { sequence(:username) { |i| "bruce#{i}" } }
|
||||||
email { sequence(:email) { |i| "bruce#{i}@wayne.com" } }
|
email { sequence(:email) { |i| "bruce#{i}@wayne.com" } }
|
||||||
password 'myawesomepassword'
|
password "myawesomepassword"
|
||||||
trust_level TrustLevel[1]
|
trust_level TrustLevel[1]
|
||||||
ip_address { sequence(:ip_address) { |i| "99.232.23.#{i % 254}" } }
|
ip_address { sequence(:ip_address) { |i| "99.232.23.#{i % 254}" } }
|
||||||
active true
|
active true
|
||||||
|
@ -18,31 +17,31 @@ Fabricator(:user_with_secondary_email, from: :user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:coding_horror, from: :user) do
|
Fabricator(:coding_horror, from: :user) do
|
||||||
name 'Coding Horror'
|
name "Coding Horror"
|
||||||
username 'CodingHorror'
|
username "CodingHorror"
|
||||||
email 'jeff@somewhere.com'
|
email "jeff@somewhere.com"
|
||||||
password 'mymoreawesomepassword'
|
password "mymoreawesomepassword"
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:evil_trout, from: :user) do
|
Fabricator(:evil_trout, from: :user) do
|
||||||
name 'Evil Trout'
|
name "Evil Trout"
|
||||||
username 'eviltrout'
|
username "eviltrout"
|
||||||
email 'eviltrout@somewhere.com'
|
email "eviltrout@somewhere.com"
|
||||||
password 'imafish123'
|
password "imafish123"
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:walter_white, from: :user) do
|
Fabricator(:walter_white, from: :user) do
|
||||||
name 'Walter White'
|
name "Walter White"
|
||||||
username 'heisenberg'
|
username "heisenberg"
|
||||||
email 'wwhite@bluemeth.com'
|
email "wwhite@bluemeth.com"
|
||||||
password 'letscook123'
|
password "letscook123"
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:inactive_user, from: :user) do
|
Fabricator(:inactive_user, from: :user) do
|
||||||
name 'Inactive User'
|
name "Inactive User"
|
||||||
username 'inactive_user'
|
username "inactive_user"
|
||||||
email 'inactive@idontexist.com'
|
email "inactive@idontexist.com"
|
||||||
password 'qwerqwer123'
|
password "qwerqwer123"
|
||||||
active false
|
active false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ Fabricator(:moderator, from: :user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:admin, from: :user) do
|
Fabricator(:admin, from: :user) do
|
||||||
name 'Anne Admin'
|
name "Anne Admin"
|
||||||
username { sequence(:username) { |i| "anne#{i}" } }
|
username { sequence(:username) { |i| "anne#{i}" } }
|
||||||
email { sequence(:email) { |i| "anne#{i}@discourse.org" } }
|
email { sequence(:email) { |i| "anne#{i}@discourse.org" } }
|
||||||
admin true
|
admin true
|
||||||
|
@ -71,17 +70,17 @@ Fabricator(:admin, from: :user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:newuser, from: :user) do
|
Fabricator(:newuser, from: :user) do
|
||||||
name 'Newbie Newperson'
|
name "Newbie Newperson"
|
||||||
username 'newbie'
|
username "newbie"
|
||||||
email 'newbie@new.com'
|
email "newbie@new.com"
|
||||||
trust_level TrustLevel[0]
|
trust_level TrustLevel[0]
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:active_user, from: :user) do
|
Fabricator(:active_user, from: :user) do
|
||||||
name 'Luke Skywalker'
|
name "Luke Skywalker"
|
||||||
username { sequence(:username) { |i| "luke#{i}" } }
|
username { sequence(:username) { |i| "luke#{i}" } }
|
||||||
email { sequence(:email) { |i| "luke#{i}@skywalker.com" } }
|
email { sequence(:email) { |i| "luke#{i}@skywalker.com" } }
|
||||||
password 'myawesomepassword'
|
password "myawesomepassword"
|
||||||
trust_level TrustLevel[1]
|
trust_level TrustLevel[1]
|
||||||
|
|
||||||
after_create do |user|
|
after_create do |user|
|
||||||
|
@ -91,29 +90,25 @@ Fabricator(:active_user, from: :user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:leader, from: :user) do
|
Fabricator(:leader, from: :user) do
|
||||||
name 'Veteran McVeteranish'
|
name "Veteran McVeteranish"
|
||||||
username { sequence(:username) { |i| "leader#{i}" } }
|
username { sequence(:username) { |i| "leader#{i}" } }
|
||||||
email { sequence(:email) { |i| "leader#{i}@leaderfun.com" } }
|
email { sequence(:email) { |i| "leader#{i}@leaderfun.com" } }
|
||||||
trust_level TrustLevel[3]
|
trust_level TrustLevel[3]
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:trust_level_0, from: :user) do
|
Fabricator(:trust_level_0, from: :user) { trust_level TrustLevel[0] }
|
||||||
trust_level TrustLevel[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:trust_level_1, from: :user) do
|
Fabricator(:trust_level_1, from: :user) { trust_level TrustLevel[1] }
|
||||||
trust_level TrustLevel[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:trust_level_4, from: :user) do
|
Fabricator(:trust_level_4, from: :user) do
|
||||||
name 'Leader McElderson'
|
name "Leader McElderson"
|
||||||
username { sequence(:username) { |i| "tl4#{i}" } }
|
username { sequence(:username) { |i| "tl4#{i}" } }
|
||||||
email { sequence(:email) { |i| "tl4#{i}@elderfun.com" } }
|
email { sequence(:email) { |i| "tl4#{i}@elderfun.com" } }
|
||||||
trust_level TrustLevel[4]
|
trust_level TrustLevel[4]
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:anonymous, from: :user) do
|
Fabricator(:anonymous, from: :user) do
|
||||||
name ''
|
name ""
|
||||||
username { sequence(:username) { |i| "anonymous#{i}" } }
|
username { sequence(:username) { |i| "anonymous#{i}" } }
|
||||||
email { sequence(:email) { |i| "anonymous#{i}@anonymous.com" } }
|
email { sequence(:email) { |i| "anonymous#{i}@anonymous.com" } }
|
||||||
trust_level TrustLevel[1]
|
trust_level TrustLevel[1]
|
||||||
|
@ -127,13 +122,9 @@ Fabricator(:anonymous, from: :user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:staged, from: :user) do
|
Fabricator(:staged, from: :user) { staged true }
|
||||||
staged true
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:unicode_user, from: :user) do
|
Fabricator(:unicode_user, from: :user) { username { sequence(:username) { |i| "Löwe#{i}" } } }
|
||||||
username { sequence(:username) { |i| "Löwe#{i}" } }
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:bot, from: :user) do
|
Fabricator(:bot, from: :user) do
|
||||||
id do
|
id do
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
Fabricator(:user_field) do
|
Fabricator(:user_field) do
|
||||||
name { sequence(:name) { |i| "field_#{i}" } }
|
name { sequence(:name) { |i| "field_#{i}" } }
|
||||||
description "user field description"
|
description "user field description"
|
||||||
field_type 'text'
|
field_type "text"
|
||||||
editable true
|
editable true
|
||||||
required true
|
required true
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:user_history) do
|
Fabricator(:user_history) {}
|
||||||
end
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:user_option) do
|
Fabricator(:user_option) {}
|
||||||
end
|
|
||||||
|
|
|
@ -5,6 +5,4 @@ Fabricator(:user_profile) do
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:user_profile_long, from: :user_profile) do
|
Fabricator(:user_profile_long, from: :user_profile) { bio_raw ("trout" * 1000) }
|
||||||
bio_raw ("trout" * 1000)
|
|
||||||
end
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
Fabricator(:user_second_factor_totp, from: :user_second_factor) do
|
Fabricator(:user_second_factor_totp, from: :user_second_factor) do
|
||||||
user
|
user
|
||||||
data 'rcyryaqage3jexfj'
|
data "rcyryaqage3jexfj"
|
||||||
enabled true
|
enabled true
|
||||||
method UserSecondFactor.methods[:totp]
|
method UserSecondFactor.methods[:totp]
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,8 +5,12 @@ Fabricator(:user_security_key) do
|
||||||
# Note: these values are valid and decode to a credential ID and COSE public key
|
# Note: these values are valid and decode to a credential ID and COSE public key
|
||||||
# HOWEVER they are largely useless unless you have the device that created
|
# HOWEVER they are largely useless unless you have the device that created
|
||||||
# them. It is nice to have an approximation though.
|
# them. It is nice to have an approximation though.
|
||||||
credential_id { 'mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A==' }
|
credential_id do
|
||||||
public_key { 'pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU=' }
|
"mJAJ4CznTO0SuLkJbYwpgK75ao4KMNIPlU5KWM92nq39kRbXzI9mSv6GxTcsMYoiPgaouNw7b7zBiS4vsQaO6A=="
|
||||||
|
end
|
||||||
|
public_key do
|
||||||
|
"pQECAyYgASFYIMNgw4GCpwBUlR2SznJ1yY7B9yFvsuxhfo+C9kcA4IitIlggRdofrCezymy2B/YarX+gfB6gZKg648/cHIMjf6wWmmU="
|
||||||
|
end
|
||||||
enabled true
|
enabled true
|
||||||
factor_type { UserSecurityKey.factor_types[:second_factor] }
|
factor_type { UserSecurityKey.factor_types[:second_factor] }
|
||||||
name { sequence(:name) { |i| "Security Key #{i + 1}" } }
|
name { sequence(:name) { |i| "Security Key #{i + 1}" } }
|
||||||
|
|
|
@ -1,62 +1,48 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Fabricator(:web_hook) do
|
Fabricator(:web_hook) do
|
||||||
payload_url 'https://meta.discourse.org/webhook_listener'
|
payload_url "https://meta.discourse.org/webhook_listener"
|
||||||
content_type WebHook.content_types['application/json']
|
content_type WebHook.content_types["application/json"]
|
||||||
wildcard_web_hook false
|
wildcard_web_hook false
|
||||||
secret 'my_lovely_secret_for_web_hook'
|
secret "my_lovely_secret_for_web_hook"
|
||||||
verify_certificate true
|
verify_certificate true
|
||||||
active true
|
active true
|
||||||
|
|
||||||
transient post_hook: WebHookEventType.find_by(name: 'post')
|
transient post_hook: WebHookEventType.find_by(name: "post")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build { |web_hook, transients| web_hook.web_hook_event_types << transients[:post_hook] }
|
||||||
web_hook.web_hook_event_types << transients[:post_hook]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:inactive_web_hook, from: :web_hook) do
|
Fabricator(:inactive_web_hook, from: :web_hook) { active false }
|
||||||
active false
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:wildcard_web_hook, from: :web_hook) do
|
Fabricator(:wildcard_web_hook, from: :web_hook) { wildcard_web_hook true }
|
||||||
wildcard_web_hook true
|
|
||||||
end
|
|
||||||
|
|
||||||
Fabricator(:topic_web_hook, from: :web_hook) do
|
Fabricator(:topic_web_hook, from: :web_hook) do
|
||||||
transient topic_hook: WebHookEventType.find_by(name: 'topic')
|
transient topic_hook: WebHookEventType.find_by(name: "topic")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:topic_hook]] }
|
||||||
web_hook.web_hook_event_types = [transients[:topic_hook]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:post_web_hook, from: :web_hook) do
|
Fabricator(:post_web_hook, from: :web_hook) do
|
||||||
transient topic_hook: WebHookEventType.find_by(name: 'post')
|
transient topic_hook: WebHookEventType.find_by(name: "post")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:post_hook]] }
|
||||||
web_hook.web_hook_event_types = [transients[:post_hook]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:user_web_hook, from: :web_hook) do
|
Fabricator(:user_web_hook, from: :web_hook) do
|
||||||
transient user_hook: WebHookEventType.find_by(name: 'user')
|
transient user_hook: WebHookEventType.find_by(name: "user")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:user_hook]] }
|
||||||
web_hook.web_hook_event_types = [transients[:user_hook]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:group_web_hook, from: :web_hook) do
|
Fabricator(:group_web_hook, from: :web_hook) do
|
||||||
transient group_hook: WebHookEventType.find_by(name: 'group')
|
transient group_hook: WebHookEventType.find_by(name: "group")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:group_hook]] }
|
||||||
web_hook.web_hook_event_types = [transients[:group_hook]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:category_web_hook, from: :web_hook) do
|
Fabricator(:category_web_hook, from: :web_hook) do
|
||||||
transient category_hook: WebHookEventType.find_by(name: 'category')
|
transient category_hook: WebHookEventType.find_by(name: "category")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build do |web_hook, transients|
|
||||||
web_hook.web_hook_event_types = [transients[:category_hook]]
|
web_hook.web_hook_event_types = [transients[:category_hook]]
|
||||||
|
@ -64,15 +50,13 @@ Fabricator(:category_web_hook, from: :web_hook) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:tag_web_hook, from: :web_hook) do
|
Fabricator(:tag_web_hook, from: :web_hook) do
|
||||||
transient tag_hook: WebHookEventType.find_by(name: 'tag')
|
transient tag_hook: WebHookEventType.find_by(name: "tag")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:tag_hook]] }
|
||||||
web_hook.web_hook_event_types = [transients[:tag_hook]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:reviewable_web_hook, from: :web_hook) do
|
Fabricator(:reviewable_web_hook, from: :web_hook) do
|
||||||
transient reviewable_hook: WebHookEventType.find_by(name: 'reviewable')
|
transient reviewable_hook: WebHookEventType.find_by(name: "reviewable")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build do |web_hook, transients|
|
||||||
web_hook.web_hook_event_types = [transients[:reviewable_hook]]
|
web_hook.web_hook_event_types = [transients[:reviewable_hook]]
|
||||||
|
@ -80,7 +64,7 @@ Fabricator(:reviewable_web_hook, from: :web_hook) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:notification_web_hook, from: :web_hook) do
|
Fabricator(:notification_web_hook, from: :web_hook) do
|
||||||
transient notification_hook: WebHookEventType.find_by(name: 'notification')
|
transient notification_hook: WebHookEventType.find_by(name: "notification")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build do |web_hook, transients|
|
||||||
web_hook.web_hook_event_types = [transients[:notification_hook]]
|
web_hook.web_hook_event_types = [transients[:notification_hook]]
|
||||||
|
@ -88,7 +72,7 @@ Fabricator(:notification_web_hook, from: :web_hook) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:user_badge_web_hook, from: :web_hook) do
|
Fabricator(:user_badge_web_hook, from: :web_hook) do
|
||||||
transient user_badge_hook: WebHookEventType.find_by(name: 'user_badge')
|
transient user_badge_hook: WebHookEventType.find_by(name: "user_badge")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build do |web_hook, transients|
|
||||||
web_hook.web_hook_event_types = [transients[:user_badge_hook]]
|
web_hook.web_hook_event_types = [transients[:user_badge_hook]]
|
||||||
|
@ -96,7 +80,7 @@ Fabricator(:user_badge_web_hook, from: :web_hook) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:group_user_web_hook, from: :web_hook) do
|
Fabricator(:group_user_web_hook, from: :web_hook) do
|
||||||
transient group_user_hook: WebHookEventType.find_by(name: 'group_user')
|
transient group_user_hook: WebHookEventType.find_by(name: "group_user")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build do |web_hook, transients|
|
||||||
web_hook.web_hook_event_types = [transients[:group_user_hook]]
|
web_hook.web_hook_event_types = [transients[:group_user_hook]]
|
||||||
|
@ -104,15 +88,13 @@ Fabricator(:group_user_web_hook, from: :web_hook) do
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:like_web_hook, from: :web_hook) do
|
Fabricator(:like_web_hook, from: :web_hook) do
|
||||||
transient like_hook: WebHookEventType.find_by(name: 'like')
|
transient like_hook: WebHookEventType.find_by(name: "like")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build { |web_hook, transients| web_hook.web_hook_event_types = [transients[:like_hook]] }
|
||||||
web_hook.web_hook_event_types = [transients[:like_hook]]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Fabricator(:user_promoted_web_hook, from: :web_hook) do
|
Fabricator(:user_promoted_web_hook, from: :web_hook) do
|
||||||
transient user_promoted_hook: WebHookEventType.find_by(name: 'user_promoted')
|
transient user_promoted_hook: WebHookEventType.find_by(name: "user_promoted")
|
||||||
|
|
||||||
after_build do |web_hook, transients|
|
after_build do |web_hook, transients|
|
||||||
web_hook.web_hook_event_types = [transients[:user_promoted_hook]]
|
web_hook.web_hook_event_types = [transients[:user_promoted_hook]]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class DropEmailLogs < ActiveRecord::Migration[5.2]
|
class DropEmailLogs < ActiveRecord::Migration[5.2]
|
||||||
DROPPED_TABLES ||= %i{email_logs}
|
DROPPED_TABLES ||= %i[email_logs]
|
||||||
|
|
||||||
def change
|
def change
|
||||||
drop_table :email_logs
|
drop_table :email_logs
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class DropPostColumns < ActiveRecord::Migration[5.2]
|
class DropPostColumns < ActiveRecord::Migration[5.2]
|
||||||
DROPPED_COLUMNS ||= {
|
DROPPED_COLUMNS ||= { posts: %i[via_email raw_email] }
|
||||||
posts: %i{via_email raw_email}
|
|
||||||
}
|
|
||||||
|
|
||||||
def up
|
def up
|
||||||
remove_column :posts, :via_email
|
remove_column :posts, :via_email
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class DropEmailLogsTable < ActiveRecord::Migration[5.2]
|
class DropEmailLogsTable < ActiveRecord::Migration[5.2]
|
||||||
DROPPED_TABLES ||= %i{email_logs}
|
DROPPED_TABLES ||= %i[email_logs]
|
||||||
|
|
||||||
def up
|
def up
|
||||||
drop_table :email_logs
|
drop_table :email_logs
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
# authors: xrav3nz
|
# authors: xrav3nz
|
||||||
|
|
||||||
extend_content_security_policy(
|
extend_content_security_policy(
|
||||||
script_src: ['https://from-plugin.com', '/local/path'],
|
script_src: %w[https://from-plugin.com /local/path],
|
||||||
object_src: ['https://test-stripping.com'],
|
object_src: ["https://test-stripping.com"],
|
||||||
frame_ancestors: ['https://frame-ancestors-plugin.ext'],
|
frame_ancestors: ["https://frame-ancestors-plugin.ext"],
|
||||||
manifest_src: ['https://manifest-src.com']
|
manifest_src: ["https://manifest-src.com"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
# version: 0.1
|
# version: 0.1
|
||||||
# authors: Frank Zappa
|
# authors: Frank Zappa
|
||||||
|
|
||||||
auth_provider title: 'with Ubuntu',
|
auth_provider title: "with Ubuntu", authenticator: Auth::FacebookAuthenticator.new
|
||||||
authenticator: Auth::FacebookAuthenticator.new
|
|
||||||
|
|
||||||
register_javascript <<JS
|
register_javascript <<JS
|
||||||
console.log("Hello world")
|
console.log("Hello world")
|
||||||
|
|
|
@ -13,7 +13,8 @@ RSpec.describe ApplicationHelper do
|
||||||
it "sends crawler content to old mobiles" do
|
it "sends crawler content to old mobiles" do
|
||||||
controller.stubs(:use_crawler_layout?).returns(false)
|
controller.stubs(:use_crawler_layout?).returns(false)
|
||||||
|
|
||||||
helper.request.user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"
|
helper.request.user_agent =
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"
|
||||||
|
|
||||||
expect(helper.include_crawler_content?).to eq(true)
|
expect(helper.include_crawler_content?).to eq(true)
|
||||||
end
|
end
|
||||||
|
@ -21,7 +22,8 @@ RSpec.describe ApplicationHelper do
|
||||||
it "does not send crawler content to new mobiles" do
|
it "does not send crawler content to new mobiles" do
|
||||||
controller.stubs(:use_crawler_layout?).returns(false)
|
controller.stubs(:use_crawler_layout?).returns(false)
|
||||||
|
|
||||||
helper.request.user_agent = "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Mobile Safari/537.36 (compatible"
|
helper.request.user_agent =
|
||||||
|
"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Mobile Safari/537.36 (compatible"
|
||||||
|
|
||||||
expect(helper.include_crawler_content?).to eq(false)
|
expect(helper.include_crawler_content?).to eq(false)
|
||||||
end
|
end
|
||||||
|
@ -29,94 +31,100 @@ RSpec.describe ApplicationHelper do
|
||||||
it "provides brotli links to brotli cdn" do
|
it "provides brotli links to brotli cdn" do
|
||||||
set_cdn_url "https://awesome.com"
|
set_cdn_url "https://awesome.com"
|
||||||
|
|
||||||
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'br'
|
helper.request.env["HTTP_ACCEPT_ENCODING"] = "br"
|
||||||
link = helper.preload_script('discourse')
|
link = helper.preload_script("discourse")
|
||||||
|
|
||||||
expect(link).to eq(script_tag("https://awesome.com/brotli_asset/discourse.js"))
|
expect(link).to eq(script_tag("https://awesome.com/brotli_asset/discourse.js"))
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with s3 CDN" do
|
context "with s3 CDN" do
|
||||||
before do
|
before do
|
||||||
global_setting :s3_bucket, 'test_bucket'
|
global_setting :s3_bucket, "test_bucket"
|
||||||
global_setting :s3_region, 'ap-australia'
|
global_setting :s3_region, "ap-australia"
|
||||||
global_setting :s3_access_key_id, '123'
|
global_setting :s3_access_key_id, "123"
|
||||||
global_setting :s3_secret_access_key, '123'
|
global_setting :s3_secret_access_key, "123"
|
||||||
global_setting :s3_cdn_url, 'https://s3cdn.com'
|
global_setting :s3_cdn_url, "https://s3cdn.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deals correctly with subfolder" do
|
it "deals correctly with subfolder" do
|
||||||
set_subfolder "/community"
|
set_subfolder "/community"
|
||||||
expect(helper.preload_script("discourse")).to include("https://s3cdn.com/assets/discourse.js")
|
expect(helper.preload_script("discourse")).to include(
|
||||||
|
"https://s3cdn.com/assets/discourse.js",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "replaces cdn URLs with s3 cdn subfolder paths" do
|
it "replaces cdn URLs with s3 cdn subfolder paths" do
|
||||||
global_setting :s3_cdn_url, 'https://s3cdn.com/s3_subpath'
|
global_setting :s3_cdn_url, "https://s3cdn.com/s3_subpath"
|
||||||
set_cdn_url "https://awesome.com"
|
set_cdn_url "https://awesome.com"
|
||||||
set_subfolder "/community"
|
set_subfolder "/community"
|
||||||
expect(helper.preload_script("discourse")).to include("https://s3cdn.com/s3_subpath/assets/discourse.js")
|
expect(helper.preload_script("discourse")).to include(
|
||||||
|
"https://s3cdn.com/s3_subpath/assets/discourse.js",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns magic brotli mangling for brotli requests" do
|
it "returns magic brotli mangling for brotli requests" do
|
||||||
|
helper.request.env["HTTP_ACCEPT_ENCODING"] = "br"
|
||||||
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'br'
|
link = helper.preload_script("discourse")
|
||||||
link = helper.preload_script('discourse')
|
|
||||||
|
|
||||||
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.br.js"))
|
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.br.js"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives s3 cdn if asset host is not set" do
|
it "gives s3 cdn if asset host is not set" do
|
||||||
link = helper.preload_script('discourse')
|
link = helper.preload_script("discourse")
|
||||||
|
|
||||||
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
|
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can fall back to gzip compression" do
|
it "can fall back to gzip compression" do
|
||||||
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'gzip'
|
helper.request.env["HTTP_ACCEPT_ENCODING"] = "gzip"
|
||||||
link = helper.preload_script('discourse')
|
link = helper.preload_script("discourse")
|
||||||
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.gz.js"))
|
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.gz.js"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives s3 cdn even if asset host is set" do
|
it "gives s3 cdn even if asset host is set" do
|
||||||
set_cdn_url "https://awesome.com"
|
set_cdn_url "https://awesome.com"
|
||||||
link = helper.preload_script('discourse')
|
link = helper.preload_script("discourse")
|
||||||
|
|
||||||
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
|
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse.js"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives s3 cdn but without brotli/gzip extensions for theme tests assets" do
|
it "gives s3 cdn but without brotli/gzip extensions for theme tests assets" do
|
||||||
helper.request.env["HTTP_ACCEPT_ENCODING"] = 'gzip, br'
|
helper.request.env["HTTP_ACCEPT_ENCODING"] = "gzip, br"
|
||||||
link = helper.preload_script('discourse/tests/theme_qunit_ember_jquery')
|
link = helper.preload_script("discourse/tests/theme_qunit_ember_jquery")
|
||||||
expect(link).to eq(script_tag("https://s3cdn.com/assets/discourse/tests/theme_qunit_ember_jquery.js"))
|
expect(link).to eq(
|
||||||
|
script_tag("https://s3cdn.com/assets/discourse/tests/theme_qunit_ember_jquery.js"),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "uses separate asset CDN if configured" do
|
it "uses separate asset CDN if configured" do
|
||||||
global_setting :s3_asset_cdn_url, "https://s3-asset-cdn.example.com"
|
global_setting :s3_asset_cdn_url, "https://s3-asset-cdn.example.com"
|
||||||
expect(helper.preload_script("discourse")).to include("https://s3-asset-cdn.example.com/assets/discourse.js")
|
expect(helper.preload_script("discourse")).to include(
|
||||||
|
"https://s3-asset-cdn.example.com/assets/discourse.js",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "add_resource_preload_list" do
|
describe "add_resource_preload_list" do
|
||||||
it "adds resources to the preload list when it's available" do
|
it "adds resources to the preload list when it's available" do
|
||||||
@links_to_preload = []
|
@links_to_preload = []
|
||||||
add_resource_preload_list('/assets/discourse.js', 'script')
|
add_resource_preload_list("/assets/discourse.js", "script")
|
||||||
add_resource_preload_list('/assets/discourse.css', 'style')
|
add_resource_preload_list("/assets/discourse.css", "style")
|
||||||
|
|
||||||
expect(@links_to_preload.size).to eq(2)
|
expect(@links_to_preload.size).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't add resources to the preload list when it's not available" do
|
it "doesn't add resources to the preload list when it's not available" do
|
||||||
@links_to_preload = nil
|
@links_to_preload = nil
|
||||||
add_resource_preload_list('/assets/discourse.js', 'script')
|
add_resource_preload_list("/assets/discourse.js", "script")
|
||||||
add_resource_preload_list('/assets/discourse.css', 'style')
|
add_resource_preload_list("/assets/discourse.css", "style")
|
||||||
|
|
||||||
expect(@links_to_preload).to eq(nil)
|
expect(@links_to_preload).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "adds resources to the preload list when preload_script is called" do
|
it "adds resources to the preload list when preload_script is called" do
|
||||||
@links_to_preload = []
|
@links_to_preload = []
|
||||||
helper.preload_script('discourse')
|
helper.preload_script("discourse")
|
||||||
|
|
||||||
expect(@links_to_preload.size).to eq(1)
|
expect(@links_to_preload.size).to eq(1)
|
||||||
end
|
end
|
||||||
|
@ -131,7 +139,7 @@ RSpec.describe ApplicationHelper do
|
||||||
it "adds resources as the correct type" do
|
it "adds resources as the correct type" do
|
||||||
@links_to_preload = []
|
@links_to_preload = []
|
||||||
helper.discourse_stylesheet_link_tag(:desktop)
|
helper.discourse_stylesheet_link_tag(:desktop)
|
||||||
helper.preload_script('discourse')
|
helper.preload_script("discourse")
|
||||||
|
|
||||||
expect(@links_to_preload[0]).to match(/as="style"/)
|
expect(@links_to_preload[0]).to match(/as="style"/)
|
||||||
expect(@links_to_preload[1]).to match(/as="script"/)
|
expect(@links_to_preload[1]).to match(/as="script"/)
|
||||||
|
@ -152,14 +160,14 @@ RSpec.describe ApplicationHelper do
|
||||||
context "when on homepage" do
|
context "when on homepage" do
|
||||||
it "will return sitelinks search tag" do
|
it "will return sitelinks search tag" do
|
||||||
helper.stubs(:current_page?).returns(false)
|
helper.stubs(:current_page?).returns(false)
|
||||||
helper.stubs(:current_page?).with('/').returns(true)
|
helper.stubs(:current_page?).with("/").returns(true)
|
||||||
expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
|
expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
context "when not on homepage" do
|
context "when not on homepage" do
|
||||||
it "will not return sitelinks search tag" do
|
it "will not return sitelinks search tag" do
|
||||||
helper.stubs(:current_page?).returns(true)
|
helper.stubs(:current_page?).returns(true)
|
||||||
helper.stubs(:current_page?).with('/').returns(false)
|
helper.stubs(:current_page?).with("/").returns(false)
|
||||||
helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
|
helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
|
||||||
expect(helper.render_sitelinks_search_tag).to be_nil
|
expect(helper.render_sitelinks_search_tag).to be_nil
|
||||||
end
|
end
|
||||||
|
@ -168,7 +176,7 @@ RSpec.describe ApplicationHelper do
|
||||||
context "for subfolder install" do
|
context "for subfolder install" do
|
||||||
context "when on homepage" do
|
context "when on homepage" do
|
||||||
it "will return sitelinks search tag" do
|
it "will return sitelinks search tag" do
|
||||||
Discourse.stubs(:base_path).returns('/subfolder-base-path/')
|
Discourse.stubs(:base_path).returns("/subfolder-base-path/")
|
||||||
helper.stubs(:current_page?).returns(false)
|
helper.stubs(:current_page?).returns(false)
|
||||||
helper.stubs(:current_page?).with(Discourse.base_path).returns(true)
|
helper.stubs(:current_page?).with(Discourse.base_path).returns(true)
|
||||||
expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
|
expect(helper.render_sitelinks_search_tag).to include('"@type":"SearchAction"')
|
||||||
|
@ -176,9 +184,9 @@ RSpec.describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
context "when not on homepage" do
|
context "when not on homepage" do
|
||||||
it "will not return sitelinks search tag" do
|
it "will not return sitelinks search tag" do
|
||||||
Discourse.stubs(:base_path).returns('/subfolder-base-path/')
|
Discourse.stubs(:base_path).returns("/subfolder-base-path/")
|
||||||
helper.stubs(:current_page?).returns(true)
|
helper.stubs(:current_page?).returns(true)
|
||||||
helper.stubs(:current_page?).with('/').returns(false)
|
helper.stubs(:current_page?).with("/").returns(false)
|
||||||
helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
|
helper.stubs(:current_page?).with(Discourse.base_path).returns(false)
|
||||||
expect(helper.render_sitelinks_search_tag).to be_nil
|
expect(helper.render_sitelinks_search_tag).to be_nil
|
||||||
end
|
end
|
||||||
|
@ -189,18 +197,17 @@ RSpec.describe ApplicationHelper do
|
||||||
describe "application_logo_url" do
|
describe "application_logo_url" do
|
||||||
context "when a dark color scheme is active" do
|
context "when a dark color scheme is active" do
|
||||||
before do
|
before do
|
||||||
dark_theme = Theme.create(
|
dark_theme =
|
||||||
name: "Dark",
|
Theme.create(
|
||||||
user_id: -1,
|
name: "Dark",
|
||||||
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
|
user_id: -1,
|
||||||
)
|
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
|
||||||
|
)
|
||||||
helper.request.env[:resolved_theme_id] = dark_theme.id
|
helper.request.env[:resolved_theme_id] = dark_theme.id
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when on desktop" do
|
context "when on desktop" do
|
||||||
before do
|
before { session[:mobile_view] = "0" }
|
||||||
session[:mobile_view] = '0'
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when logo_dark is not set" do
|
context "when logo_dark is not set" do
|
||||||
it "will return site_logo_url instead" do
|
it "will return site_logo_url instead" do
|
||||||
|
@ -209,9 +216,7 @@ RSpec.describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when logo_dark is set" do
|
context "when logo_dark is set" do
|
||||||
before do
|
before { SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png") }
|
||||||
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "will return site_logo_dark_url" do
|
it "will return site_logo_dark_url" do
|
||||||
expect(helper.application_logo_url).to eq(SiteSetting.site_logo_dark_url)
|
expect(helper.application_logo_url).to eq(SiteSetting.site_logo_dark_url)
|
||||||
|
@ -220,9 +225,7 @@ RSpec.describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when on mobile" do
|
context "when on mobile" do
|
||||||
before do
|
before { session[:mobile_view] = "1" }
|
||||||
session[:mobile_view] = '1'
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when mobile_logo_dark is not set" do
|
context "when mobile_logo_dark is not set" do
|
||||||
it "will return site_mobile_logo_url instead" do
|
it "will return site_mobile_logo_url instead" do
|
||||||
|
@ -232,7 +235,7 @@ RSpec.describe ApplicationHelper do
|
||||||
|
|
||||||
context "when mobile_logo_dark is set" do
|
context "when mobile_logo_dark is set" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.mobile_logo_dark = Fabricate(:upload, url: '/images/mobile-logo-dark.png')
|
SiteSetting.mobile_logo_dark = Fabricate(:upload, url: "/images/mobile-logo-dark.png")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "will return site_mobile_logo_dark_url" do
|
it "will return site_mobile_logo_dark_url" do
|
||||||
|
@ -254,11 +257,12 @@ RSpec.describe ApplicationHelper do
|
||||||
|
|
||||||
context "when dark theme is present" do
|
context "when dark theme is present" do
|
||||||
before do
|
before do
|
||||||
_dark_theme = Theme.create(
|
_dark_theme =
|
||||||
name: "Dark",
|
Theme.create(
|
||||||
user_id: -1,
|
name: "Dark",
|
||||||
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
|
user_id: -1,
|
||||||
)
|
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when dark logo is not present" do
|
context "when dark logo is not present" do
|
||||||
|
@ -268,9 +272,7 @@ RSpec.describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when dark logo is present" do
|
context "when dark logo is present" do
|
||||||
before do
|
before { SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png") }
|
||||||
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return correct url" do
|
it "should return correct url" do
|
||||||
expect(helper.application_logo_dark_url).to eq(SiteSetting.site_logo_dark_url)
|
expect(helper.application_logo_dark_url).to eq(SiteSetting.site_logo_dark_url)
|
||||||
|
@ -280,13 +282,14 @@ RSpec.describe ApplicationHelper do
|
||||||
|
|
||||||
context "when dark theme is present and selected" do
|
context "when dark theme is present and selected" do
|
||||||
before do
|
before do
|
||||||
dark_theme = Theme.create(
|
dark_theme =
|
||||||
name: "Dark",
|
Theme.create(
|
||||||
user_id: -1,
|
name: "Dark",
|
||||||
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
|
user_id: -1,
|
||||||
)
|
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
|
||||||
|
)
|
||||||
helper.request.env[:resolved_theme_id] = dark_theme.id
|
helper.request.env[:resolved_theme_id] = dark_theme.id
|
||||||
SiteSetting.logo_dark = Fabricate(:upload, url: '/images/logo-dark.png')
|
SiteSetting.logo_dark = Fabricate(:upload, url: "/images/logo-dark.png")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return nothing" do
|
it "should return nothing" do
|
||||||
|
@ -298,119 +301,168 @@ RSpec.describe ApplicationHelper do
|
||||||
|
|
||||||
describe "#mobile_view?" do
|
describe "#mobile_view?" do
|
||||||
context "when enable_mobile_theme is true" do
|
context "when enable_mobile_theme is true" do
|
||||||
before do
|
before { SiteSetting.enable_mobile_theme = true }
|
||||||
SiteSetting.enable_mobile_theme = true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is true if mobile_view is '1' in the session" do
|
it "is true if mobile_view is '1' in the session" do
|
||||||
session[:mobile_view] = '1'
|
session[:mobile_view] = "1"
|
||||||
expect(helper.mobile_view?).to eq(true)
|
expect(helper.mobile_view?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false if mobile_view is '0' in the session" do
|
it "is false if mobile_view is '0' in the session" do
|
||||||
session[:mobile_view] = '0'
|
session[:mobile_view] = "0"
|
||||||
expect(helper.mobile_view?).to eq(false)
|
expect(helper.mobile_view?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when mobile_view session is cleared" do
|
context "when mobile_view session is cleared" do
|
||||||
before do
|
before { params[:mobile_view] = "auto" }
|
||||||
params[:mobile_view] = 'auto'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is false if user agent is not mobile" do
|
it "is false if user agent is not mobile" do
|
||||||
session[:mobile_view] = '1'
|
session[:mobile_view] = "1"
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to be_falsey
|
expect(helper.mobile_view?).to be_falsey
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is true for iPhone" do
|
it "is true for iPhone" do
|
||||||
session[:mobile_view] = '0'
|
session[:mobile_view] = "0"
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to eq(true)
|
expect(helper.mobile_view?).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when mobile_view is not set" do
|
context "when mobile_view is not set" do
|
||||||
it "is false if user agent is not mobile" do
|
it "is false if user agent is not mobile" do
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to be_falsey
|
expect(helper.mobile_view?).to be_falsey
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is true for iPhone" do
|
it "is true for iPhone" do
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to eq(true)
|
expect(helper.mobile_view?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is true for Android Samsung Galaxy" do
|
it "is true for Android Samsung Galaxy" do
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to eq(true)
|
expect(helper.mobile_view?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is true for Android Google Nexus 5X" do
|
it "is true for Android Google Nexus 5X" do
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.43 Mobile Safari/537.36')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.43 Mobile Safari/537.36",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to eq(true)
|
expect(helper.mobile_view?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false for iPad" do
|
it "is false for iPad" do
|
||||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B14 3 Safari/601.1")
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B14 3 Safari/601.1",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to eq(false)
|
expect(helper.mobile_view?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false for Nexus 10 tablet" do
|
it "is false for Nexus 10 tablet" do
|
||||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 5.1.1; Nexus 10 Build/LMY49G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36")
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 10 Build/LMY49G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to be_falsey
|
expect(helper.mobile_view?).to be_falsey
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false for Nexus 7 tablet" do
|
it "is false for Nexus 7 tablet" do
|
||||||
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MMB29Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36")
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MMB29Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Safari/537.36",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to be_falsey
|
expect(helper.mobile_view?).to be_falsey
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when enable_mobile_theme is false" do
|
context "when enable_mobile_theme is false" do
|
||||||
before do
|
before { SiteSetting.enable_mobile_theme = false }
|
||||||
SiteSetting.enable_mobile_theme = false
|
|
||||||
end
|
|
||||||
|
|
||||||
it "is false if mobile_view is '1' in the session" do
|
it "is false if mobile_view is '1' in the session" do
|
||||||
session[:mobile_view] = '1'
|
session[:mobile_view] = "1"
|
||||||
expect(helper.mobile_view?).to eq(false)
|
expect(helper.mobile_view?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false if mobile_view is '0' in the session" do
|
it "is false if mobile_view is '0' in the session" do
|
||||||
session[:mobile_view] = '0'
|
session[:mobile_view] = "0"
|
||||||
expect(helper.mobile_view?).to eq(false)
|
expect(helper.mobile_view?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when mobile_view is not set" do
|
context "when mobile_view is not set" do
|
||||||
it "is false if user agent is not mobile" do
|
it "is false if user agent is not mobile" do
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to eq(false)
|
expect(helper.mobile_view?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false for iPhone" do
|
it "is false for iPhone" do
|
||||||
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5')
|
controller
|
||||||
|
.request
|
||||||
|
.stubs(:user_agent)
|
||||||
|
.returns(
|
||||||
|
"Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5",
|
||||||
|
)
|
||||||
expect(helper.mobile_view?).to eq(false)
|
expect(helper.mobile_view?).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#html_classes' do
|
describe "#html_classes" do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
it "includes 'rtl' when the I18n.locale is rtl" do
|
it "includes 'rtl' when the I18n.locale is rtl" do
|
||||||
I18n.stubs(:locale).returns(:he)
|
I18n.stubs(:locale).returns(:he)
|
||||||
expect(helper.html_classes.split(" ")).to include('rtl')
|
expect(helper.html_classes.split(" ")).to include("rtl")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an empty string when the I18n.locale is not rtl' do
|
it "returns an empty string when the I18n.locale is not rtl" do
|
||||||
I18n.stubs(:locale).returns(:zh_TW)
|
I18n.stubs(:locale).returns(:zh_TW)
|
||||||
expect(helper.html_classes.split(" ")).not_to include('rtl')
|
expect(helper.html_classes.split(" ")).not_to include("rtl")
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'text size' do
|
describe "text size" do
|
||||||
context "with a user option" do
|
context "with a user option" do
|
||||||
before do
|
before do
|
||||||
user.user_option.text_size = "larger"
|
user.user_option.text_size = "larger"
|
||||||
|
@ -418,63 +470,65 @@ RSpec.describe ApplicationHelper do
|
||||||
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ignores invalid text sizes' do
|
it "ignores invalid text sizes" do
|
||||||
helper.request.cookies["text_size"] = "invalid"
|
helper.request.cookies["text_size"] = "invalid"
|
||||||
expect(helper.html_classes.split(" ")).to include('text-size-larger')
|
expect(helper.html_classes.split(" ")).to include("text-size-larger")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ignores missing text size' do
|
it "ignores missing text size" do
|
||||||
helper.request.cookies["text_size"] = nil
|
helper.request.cookies["text_size"] = nil
|
||||||
expect(helper.html_classes.split(" ")).to include('text-size-larger')
|
expect(helper.html_classes.split(" ")).to include("text-size-larger")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ignores cookies with lower sequence' do
|
it "ignores cookies with lower sequence" do
|
||||||
user.user_option.update!(text_size_seq: 2)
|
user.user_option.update!(text_size_seq: 2)
|
||||||
|
|
||||||
helper.request.cookies["text_size"] = "normal|1"
|
helper.request.cookies["text_size"] = "normal|1"
|
||||||
expect(helper.html_classes.split(" ")).to include('text-size-larger')
|
expect(helper.html_classes.split(" ")).to include("text-size-larger")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'prioritises the cookie specified text size' do
|
it "prioritises the cookie specified text size" do
|
||||||
user.user_option.update!(text_size_seq: 2)
|
user.user_option.update!(text_size_seq: 2)
|
||||||
|
|
||||||
helper.request.cookies["text_size"] = "largest|4"
|
helper.request.cookies["text_size"] = "largest|4"
|
||||||
expect(helper.html_classes.split(" ")).to include('text-size-largest')
|
expect(helper.html_classes.split(" ")).to include("text-size-largest")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes the user specified text size' do
|
it "includes the user specified text size" do
|
||||||
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
expect(helper.html_classes.split(" ")).to include('text-size-larger')
|
expect(helper.html_classes.split(" ")).to include("text-size-larger")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'falls back to the default text size for anon' do
|
it "falls back to the default text size for anon" do
|
||||||
expect(helper.html_classes.split(" ")).to include('text-size-normal')
|
expect(helper.html_classes.split(" ")).to include("text-size-normal")
|
||||||
SiteSetting.default_text_size = "largest"
|
SiteSetting.default_text_size = "largest"
|
||||||
expect(helper.html_classes.split(" ")).to include('text-size-largest')
|
expect(helper.html_classes.split(" ")).to include("text-size-largest")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "includes 'anon' for anonymous users and excludes when logged in" do
|
it "includes 'anon' for anonymous users and excludes when logged in" do
|
||||||
expect(helper.html_classes.split(" ")).to include('anon')
|
expect(helper.html_classes.split(" ")).to include("anon")
|
||||||
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
expect(helper.html_classes.split(" ")).not_to include('anon')
|
expect(helper.html_classes.split(" ")).not_to include("anon")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'gsub_emoji_to_unicode' do
|
describe "gsub_emoji_to_unicode" do
|
||||||
it "converts all emoji to unicode" do
|
it "converts all emoji to unicode" do
|
||||||
expect(helper.gsub_emoji_to_unicode('Boat Talk: my :sailboat: boat: why is it so slow? :snail:')).to eq("Boat Talk: my ⛵ boat: why is it so slow? 🐌")
|
expect(
|
||||||
|
helper.gsub_emoji_to_unicode("Boat Talk: my :sailboat: boat: why is it so slow? :snail:"),
|
||||||
|
).to eq("Boat Talk: my ⛵ boat: why is it so slow? 🐌")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'preloaded_json' do
|
describe "preloaded_json" do
|
||||||
it 'returns empty JSON if preloaded is empty' do
|
it "returns empty JSON if preloaded is empty" do
|
||||||
@preloaded = nil
|
@preloaded = nil
|
||||||
expect(helper.preloaded_json).to eq('{}')
|
expect(helper.preloaded_json).to eq("{}")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'escapes and strips invalid unicode and strips in json body' do
|
it "escapes and strips invalid unicode and strips in json body" do
|
||||||
@preloaded = { test: %{["< \x80"]} }
|
@preloaded = { test: %{["< \x80"]} }
|
||||||
expect(helper.preloaded_json).to eq(%{{"test":"[\\"\\u003c \uFFFD\\"]"}})
|
expect(helper.preloaded_json).to eq(%{{"test":"[\\"\\u003c \uFFFD\\"]"}})
|
||||||
end
|
end
|
||||||
|
@ -482,9 +536,7 @@ RSpec.describe ApplicationHelper do
|
||||||
|
|
||||||
describe "client_side_setup_data" do
|
describe "client_side_setup_data" do
|
||||||
context "when Rails.env.development? is true" do
|
context "when Rails.env.development? is true" do
|
||||||
before do
|
before { Rails.env.stubs(:development?).returns(true) }
|
||||||
Rails.env.stubs(:development?).returns(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the correct service worker url" do
|
it "returns the correct service worker url" do
|
||||||
expect(helper.client_side_setup_data[:service_worker_url]).to eq("service-worker.js")
|
expect(helper.client_side_setup_data[:service_worker_url]).to eq("service-worker.js")
|
||||||
|
@ -499,9 +551,7 @@ RSpec.describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "if the DEBUG_PRELOADED_APP_DATA env var is provided" do
|
context "if the DEBUG_PRELOADED_APP_DATA env var is provided" do
|
||||||
before do
|
before { ENV["DEBUG_PRELOADED_APP_DATA"] = "true" }
|
||||||
ENV['DEBUG_PRELOADED_APP_DATA'] = 'true'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns that key as true" do
|
it "returns that key as true" do
|
||||||
expect(helper.client_side_setup_data[:debug_preloaded_app_data]).to eq(true)
|
expect(helper.client_side_setup_data[:debug_preloaded_app_data]).to eq(true)
|
||||||
|
@ -510,54 +560,42 @@ RSpec.describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'crawlable_meta_data' do
|
describe "crawlable_meta_data" do
|
||||||
it 'Supports ASCII URLs with odd chars' do
|
it "Supports ASCII URLs with odd chars" do
|
||||||
result = helper.crawlable_meta_data(
|
result =
|
||||||
url: (+"http://localhost/ión").force_encoding("ASCII-8BIT").freeze
|
helper.crawlable_meta_data(
|
||||||
)
|
url: (+"http://localhost/ión").force_encoding("ASCII-8BIT").freeze,
|
||||||
|
)
|
||||||
|
|
||||||
expect(result).to include("ión")
|
expect(result).to include("ión")
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with opengraph image" do
|
context "with opengraph image" do
|
||||||
it 'returns the correct image' do
|
it "returns the correct image" do
|
||||||
SiteSetting.opengraph_image = Fabricate(:upload,
|
SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.png")
|
||||||
url: '/images/og-image.png'
|
|
||||||
)
|
|
||||||
|
|
||||||
SiteSetting.twitter_summary_large_image = Fabricate(:upload,
|
SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.png")
|
||||||
url: '/images/twitter.png'
|
|
||||||
)
|
|
||||||
|
|
||||||
SiteSetting.large_icon = Fabricate(:upload,
|
SiteSetting.large_icon = Fabricate(:upload, url: "/images/large_icon.png")
|
||||||
url: '/images/large_icon.png'
|
|
||||||
)
|
|
||||||
|
|
||||||
SiteSetting.apple_touch_icon = Fabricate(:upload,
|
SiteSetting.apple_touch_icon =
|
||||||
url: '/images/default-apple-touch-icon.png'
|
Fabricate(:upload, url: "/images/default-apple-touch-icon.png")
|
||||||
)
|
|
||||||
|
|
||||||
SiteSetting.logo = Fabricate(:upload, url: '/images/d-logo-sketch.png')
|
SiteSetting.logo = Fabricate(:upload, url: "/images/d-logo-sketch.png")
|
||||||
|
|
||||||
expect(
|
expect(helper.crawlable_meta_data(image: "some-image.png")).to include("some-image.png")
|
||||||
helper.crawlable_meta_data(image: "some-image.png")
|
|
||||||
).to include("some-image.png")
|
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(
|
expect(helper.crawlable_meta_data).to include(SiteSetting.site_opengraph_image_url)
|
||||||
SiteSetting.site_opengraph_image_url
|
|
||||||
)
|
|
||||||
|
|
||||||
SiteSetting.opengraph_image = nil
|
SiteSetting.opengraph_image = nil
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(
|
expect(helper.crawlable_meta_data).to include(
|
||||||
SiteSetting.site_twitter_summary_large_image_url
|
SiteSetting.site_twitter_summary_large_image_url,
|
||||||
)
|
)
|
||||||
|
|
||||||
SiteSetting.twitter_summary_large_image = nil
|
SiteSetting.twitter_summary_large_image = nil
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(
|
expect(helper.crawlable_meta_data).to include(SiteSetting.site_large_icon_url)
|
||||||
SiteSetting.site_large_icon_url
|
|
||||||
)
|
|
||||||
|
|
||||||
SiteSetting.large_icon = nil
|
SiteSetting.large_icon = nil
|
||||||
SiteSetting.logo_small = nil
|
SiteSetting.logo_small = nil
|
||||||
|
@ -566,67 +604,62 @@ RSpec.describe ApplicationHelper do
|
||||||
|
|
||||||
SiteSetting.logo = nil
|
SiteSetting.logo = nil
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(Upload.find(SiteIconManager::SKETCH_LOGO_ID).url)
|
expect(helper.crawlable_meta_data).to include(
|
||||||
|
Upload.find(SiteIconManager::SKETCH_LOGO_ID).url,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not allow SVG images for twitter:image, falls back to site logo or nothing if site logo is SVG too" do
|
it "does not allow SVG images for twitter:image, falls back to site logo or nothing if site logo is SVG too" do
|
||||||
SiteSetting.logo = Fabricate(:upload, url: '/images/d-logo-sketch.png')
|
SiteSetting.logo = Fabricate(:upload, url: "/images/d-logo-sketch.png")
|
||||||
SiteSetting.opengraph_image = Fabricate(:upload,
|
SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.png")
|
||||||
url: '/images/og-image.png'
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
||||||
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_opengraph_image_url}\" />
|
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_opengraph_image_url}\" />
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
SiteSetting.opengraph_image = Fabricate(:upload,
|
SiteSetting.opengraph_image = Fabricate(:upload, url: "/images/og-image.svg")
|
||||||
url: '/images/og-image.svg'
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
||||||
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
|
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
SiteSetting.twitter_summary_large_image = Fabricate(:upload,
|
SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.png")
|
||||||
url: '/images/twitter.png'
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
||||||
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_twitter_summary_large_image_url}\" />
|
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_twitter_summary_large_image_url}\" />
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
SiteSetting.twitter_summary_large_image = Fabricate(:upload,
|
SiteSetting.twitter_summary_large_image = Fabricate(:upload, url: "/images/twitter.svg")
|
||||||
url: '/images/twitter.svg'
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
expect(helper.crawlable_meta_data).to include(<<~HTML)
|
||||||
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
|
<meta name=\"twitter:image\" content=\"#{SiteSetting.site_logo_url}\" />
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
SiteSetting.logo = Fabricate(:upload, url: '/images/d-logo-sketch.svg')
|
SiteSetting.logo = Fabricate(:upload, url: "/images/d-logo-sketch.svg")
|
||||||
|
|
||||||
expect(helper.crawlable_meta_data).not_to include("twitter:image")
|
expect(helper.crawlable_meta_data).not_to include("twitter:image")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'discourse_color_scheme_stylesheets' do
|
describe "discourse_color_scheme_stylesheets" do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
it 'returns a stylesheet link tag by default' do
|
it "returns a stylesheet link tag by default" do
|
||||||
cs_stylesheets = helper.discourse_color_scheme_stylesheets
|
cs_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
expect(cs_stylesheets).to include("stylesheets/color_definitions")
|
expect(cs_stylesheets).to include("stylesheets/color_definitions")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns two color scheme link tags when dark mode is enabled' do
|
it "returns two color scheme link tags when dark mode is enabled" do
|
||||||
SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck_first(:id)
|
SiteSetting.default_dark_mode_color_scheme_id =
|
||||||
|
ColorScheme.where(name: "Dark").pluck_first(:id)
|
||||||
cs_stylesheets = helper.discourse_color_scheme_stylesheets
|
cs_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
|
||||||
expect(cs_stylesheets).to include("(prefers-color-scheme: dark)")
|
expect(cs_stylesheets).to include("(prefers-color-scheme: dark)")
|
||||||
expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2)
|
expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles a missing dark color scheme gracefully' do
|
it "handles a missing dark color scheme gracefully" do
|
||||||
scheme = ColorScheme.create!(name: "pyramid")
|
scheme = ColorScheme.create!(name: "pyramid")
|
||||||
SiteSetting.default_dark_mode_color_scheme_id = scheme.id
|
SiteSetting.default_dark_mode_color_scheme_id = scheme.id
|
||||||
scheme.destroy!
|
scheme.destroy!
|
||||||
|
@ -638,7 +671,7 @@ RSpec.describe ApplicationHelper do
|
||||||
|
|
||||||
context "with custom light scheme" do
|
context "with custom light scheme" do
|
||||||
before do
|
before do
|
||||||
@new_cs = Fabricate(:color_scheme, name: 'Flamboyant')
|
@new_cs = Fabricate(:color_scheme, name: "Flamboyant")
|
||||||
user.user_option.color_scheme_id = @new_cs.id
|
user.user_option.color_scheme_id = @new_cs.id
|
||||||
user.user_option.save!
|
user.user_option.save!
|
||||||
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
|
@ -673,9 +706,10 @@ RSpec.describe ApplicationHelper do
|
||||||
user.user_option.dark_scheme_id = -1
|
user.user_option.dark_scheme_id = -1
|
||||||
user.user_option.save!
|
user.user_option.save!
|
||||||
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
@new_cs = Fabricate(:color_scheme, name: 'Custom Color Scheme')
|
@new_cs = Fabricate(:color_scheme, name: "Custom Color Scheme")
|
||||||
|
|
||||||
SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck_first(:id)
|
SiteSetting.default_dark_mode_color_scheme_id =
|
||||||
|
ColorScheme.where(name: "Dark").pluck_first(:id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns no dark scheme stylesheet when user has disabled that option" do
|
it "returns no dark scheme stylesheet when user has disabled that option" do
|
||||||
|
@ -711,23 +745,24 @@ RSpec.describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "dark_color_scheme?" do
|
describe "dark_color_scheme?" do
|
||||||
it 'returns false for the base color scheme' do
|
it "returns false for the base color scheme" do
|
||||||
expect(helper.dark_color_scheme?).to eq(false)
|
expect(helper.dark_color_scheme?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'works correctly for a dark scheme' do
|
it "works correctly for a dark scheme" do
|
||||||
dark_theme = Theme.create(
|
dark_theme =
|
||||||
name: "Dark",
|
Theme.create(
|
||||||
user_id: -1,
|
name: "Dark",
|
||||||
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id
|
user_id: -1,
|
||||||
)
|
color_scheme_id: ColorScheme.find_by(base_scheme_id: "Dark").id,
|
||||||
|
)
|
||||||
helper.request.env[:resolved_theme_id] = dark_theme.id
|
helper.request.env[:resolved_theme_id] = dark_theme.id
|
||||||
|
|
||||||
expect(helper.dark_color_scheme?).to eq(true)
|
expect(helper.dark_color_scheme?).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'html_lang' do
|
describe "html_lang" do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -735,12 +770,12 @@ RSpec.describe ApplicationHelper do
|
||||||
SiteSetting.default_locale = :fr
|
SiteSetting.default_locale = :fr
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns default locale if no request' do
|
it "returns default locale if no request" do
|
||||||
helper.request = nil
|
helper.request = nil
|
||||||
expect(helper.html_lang).to eq(SiteSetting.default_locale)
|
expect(helper.html_lang).to eq(SiteSetting.default_locale)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns current user locale if request' do
|
it "returns current user locale if request" do
|
||||||
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
expect(helper.html_lang).to eq(I18n.locale.to_s)
|
expect(helper.html_lang).to eq(I18n.locale.to_s)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,12 +2,8 @@
|
||||||
|
|
||||||
module RedisSnapshotHelper
|
module RedisSnapshotHelper
|
||||||
def use_redis_snapshotting
|
def use_redis_snapshotting
|
||||||
before(:each) do
|
before(:each) { RedisSnapshot.begin_faux_transaction }
|
||||||
RedisSnapshot.begin_faux_transaction
|
|
||||||
end
|
|
||||||
|
|
||||||
after(:each) do
|
after(:each) { RedisSnapshot.end_faux_transaction }
|
||||||
RedisSnapshot.end_faux_transaction
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe TopicsHelper do
|
RSpec.describe TopicsHelper do
|
||||||
|
|
||||||
describe "#categories_breadcrumb" do
|
describe "#categories_breadcrumb" do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
let(:category) { Fabricate(:category_with_definition) }
|
let(:category) { Fabricate(:category_with_definition) }
|
||||||
let(:subcategory) { Fabricate(:category_with_definition, parent_category_id: category.id) }
|
let(:subcategory) { Fabricate(:category_with_definition, parent_category_id: category.id) }
|
||||||
let(:subsubcategory) { Fabricate(:category_with_definition, parent_category_id: subcategory.id) }
|
let(:subsubcategory) do
|
||||||
|
Fabricate(:category_with_definition, parent_category_id: subcategory.id)
|
||||||
|
end
|
||||||
|
|
||||||
it "works with sub-sub-categories" do
|
it "works with sub-sub-categories" do
|
||||||
SiteSetting.max_category_nesting = 3
|
SiteSetting.max_category_nesting = 3
|
||||||
|
|
|
@ -3,18 +3,17 @@
|
||||||
RSpec.describe UserNotificationsHelper do
|
RSpec.describe UserNotificationsHelper do
|
||||||
let(:upload_path) { Discourse.store.upload_path }
|
let(:upload_path) { Discourse.store.upload_path }
|
||||||
|
|
||||||
describe '#email_excerpt' do
|
describe "#email_excerpt" do
|
||||||
let(:paragraphs) { [
|
let(:paragraphs) do
|
||||||
"<p>This is the first paragraph, but you should read more.</p>",
|
[
|
||||||
"<p>And here is its friend, the second paragraph.</p>"
|
"<p>This is the first paragraph, but you should read more.</p>",
|
||||||
] }
|
"<p>And here is its friend, the second paragraph.</p>",
|
||||||
|
]
|
||||||
let(:cooked) do
|
|
||||||
paragraphs.join("\n")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:post_quote) do
|
let(:cooked) { paragraphs.join("\n") }
|
||||||
<<~HTML
|
|
||||||
|
let(:post_quote) { <<~HTML }
|
||||||
<aside class="quote no-group" data-post="859" data-topic="30">
|
<aside class="quote no-group" data-post="859" data-topic="30">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="quote-controls"></div>
|
<div class="quote-controls"></div>
|
||||||
|
@ -24,19 +23,16 @@ RSpec.describe UserNotificationsHelper do
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</aside>
|
</aside>
|
||||||
HTML
|
HTML
|
||||||
end
|
|
||||||
|
|
||||||
let(:image_paragraph) do
|
let(:image_paragraph) do
|
||||||
'<p><img src="//localhost:3000/uploads/b9.png" width="300" height="300"></p>'
|
'<p><img src="//localhost:3000/uploads/b9.png" width="300" height="300"></p>'
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:lightbox_image) do
|
let(:lightbox_image) { <<~HTML }
|
||||||
<<~HTML
|
|
||||||
<p><div class="lightbox-wrapper"><a class="lightbox" href="//localhost:3000/uploads/default/original/1X/123456.jpeg" data-download-href="//localhost:3000/uploads/default/123456" title="giant-meteor-2020"><img src="//localhost:3000/uploads/default/original/1X/123456.jpeg" alt="giant-meteor-2020" data-base62-sha1="3jcR88161od6Uthq1ixWKJh2ejp" width="517" height="152" data-small-upload="//localhost:3000/uploads/default/optimized/1X/123456_2_10x10.png"><div class="meta">
|
<p><div class="lightbox-wrapper"><a class="lightbox" href="//localhost:3000/uploads/default/original/1X/123456.jpeg" data-download-href="//localhost:3000/uploads/default/123456" title="giant-meteor-2020"><img src="//localhost:3000/uploads/default/original/1X/123456.jpeg" alt="giant-meteor-2020" data-base62-sha1="3jcR88161od6Uthq1ixWKJh2ejp" width="517" height="152" data-small-upload="//localhost:3000/uploads/default/optimized/1X/123456_2_10x10.png"><div class="meta">
|
||||||
<svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">giant-meteor-2020</span><span class="informations">851×251 44 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg>
|
<svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">giant-meteor-2020</span><span class="informations">851×251 44 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg>
|
||||||
</div></a></div></p>
|
</div></a></div></p>
|
||||||
HTML
|
HTML
|
||||||
end
|
|
||||||
|
|
||||||
let(:expected_lightbox_image) do
|
let(:expected_lightbox_image) do
|
||||||
'<div class="lightbox-wrapper"><a class="lightbox" href="//localhost:3000/uploads/default/original/1X/123456.jpeg" data-download-href="//localhost:3000/uploads/default/123456" title="giant-meteor-2020"><img src="//localhost:3000/uploads/default/original/1X/123456.jpeg" alt="giant-meteor-2020" data-base62-sha1="3jcR88161od6Uthq1ixWKJh2ejp" width="517" height="152" data-small-upload="//localhost:3000/uploads/default/optimized/1X/123456_2_10x10.png"></a></div>'
|
'<div class="lightbox-wrapper"><a class="lightbox" href="//localhost:3000/uploads/default/original/1X/123456.jpeg" data-download-href="//localhost:3000/uploads/default/123456" title="giant-meteor-2020"><img src="//localhost:3000/uploads/default/original/1X/123456.jpeg" alt="giant-meteor-2020" data-base62-sha1="3jcR88161od6Uthq1ixWKJh2ejp" width="517" height="152" data-small-upload="//localhost:3000/uploads/default/optimized/1X/123456_2_10x10.png"></a></div>'
|
||||||
|
@ -53,14 +49,16 @@ RSpec.describe UserNotificationsHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't count emoji images" do
|
it "doesn't count emoji images" do
|
||||||
with_emoji = "<p>Hi <img src=\"/images/emoji/twitter/smile.png?v=#{Emoji::EMOJI_VERSION}\" title=\":smile:\" class=\"emoji\" alt=\":smile:\" loading=\"lazy\" width=\"20\" height=\"20\"></p>"
|
with_emoji =
|
||||||
|
"<p>Hi <img src=\"/images/emoji/twitter/smile.png?v=#{Emoji::EMOJI_VERSION}\" title=\":smile:\" class=\"emoji\" alt=\":smile:\" loading=\"lazy\" width=\"20\" height=\"20\"></p>"
|
||||||
arg = ([with_emoji] + paragraphs).join("\n")
|
arg = ([with_emoji] + paragraphs).join("\n")
|
||||||
SiteSetting.digest_min_excerpt_length = 50
|
SiteSetting.digest_min_excerpt_length = 50
|
||||||
expect(helper.email_excerpt(arg)).to eq([with_emoji, paragraphs[0]].join)
|
expect(helper.email_excerpt(arg)).to eq([with_emoji, paragraphs[0]].join)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "only counts link text" do
|
it "only counts link text" do
|
||||||
with_link = "<p>Hi <a href=\"https://really-long.essays.com/essay/number/9000/this-one-is-about-friends-and-got-a-C-minus-in-grade-9\">friends</a>!</p>"
|
with_link =
|
||||||
|
"<p>Hi <a href=\"https://really-long.essays.com/essay/number/9000/this-one-is-about-friends-and-got-a-C-minus-in-grade-9\">friends</a>!</p>"
|
||||||
arg = ([with_link] + paragraphs).join("\n")
|
arg = ([with_link] + paragraphs).join("\n")
|
||||||
SiteSetting.digest_min_excerpt_length = 50
|
SiteSetting.digest_min_excerpt_length = 50
|
||||||
expect(helper.email_excerpt(arg)).to eq([with_link, paragraphs[0]].join)
|
expect(helper.email_excerpt(arg)).to eq([with_link, paragraphs[0]].join)
|
||||||
|
@ -81,11 +79,12 @@ RSpec.describe UserNotificationsHelper do
|
||||||
<p>AFTER</p>
|
<p>AFTER</p>
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
expect(helper.email_excerpt(cooked)).to eq "<p>BEFORE</p><blockquote>\n <p>This is a user quote</p>\n</blockquote><p>AFTER</p>"
|
expect(
|
||||||
|
helper.email_excerpt(cooked),
|
||||||
|
).to eq "<p>BEFORE</p><blockquote>\n <p>This is a user quote</p>\n</blockquote><p>AFTER</p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "defaults to content after post quote (image w/ no text)" do
|
it "defaults to content after post quote (image w/ no text)" do
|
||||||
|
|
||||||
cooked = <<~HTML
|
cooked = <<~HTML
|
||||||
#{post_quote}
|
#{post_quote}
|
||||||
#{image_paragraph}
|
#{image_paragraph}
|
||||||
|
@ -94,7 +93,8 @@ RSpec.describe UserNotificationsHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "defaults to content after post quote (onebox)" do
|
it "defaults to content after post quote (onebox)" do
|
||||||
aside_onebox = '<aside class="onebox wikipedia"><article class="onebox-body"><p>Onebox excerpt here</p></article><div class="onebox-metadata"></div></aside>'
|
aside_onebox =
|
||||||
|
'<aside class="onebox wikipedia"><article class="onebox-body"><p>Onebox excerpt here</p></article><div class="onebox-metadata"></div></aside>'
|
||||||
cooked = <<~HTML
|
cooked = <<~HTML
|
||||||
#{post_quote}
|
#{post_quote}
|
||||||
#{aside_onebox}
|
#{aside_onebox}
|
||||||
|
@ -120,44 +120,40 @@ RSpec.describe UserNotificationsHelper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#logo_url' do
|
describe "#logo_url" do
|
||||||
describe 'local store' do
|
describe "local store" do
|
||||||
let(:upload) { Fabricate(:upload, sha1: "somesha1") }
|
let(:upload) { Fabricate(:upload, sha1: "somesha1") }
|
||||||
|
|
||||||
before do
|
before { SiteSetting.logo = upload }
|
||||||
SiteSetting.logo = upload
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the right URL' do
|
it "should return the right URL" do
|
||||||
expect(helper.logo_url).to eq(
|
expect(helper.logo_url).to eq(
|
||||||
"http://test.localhost/#{upload_path}/original/1X/somesha1.png"
|
"http://test.localhost/#{upload_path}/original/1X/somesha1.png",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when cdn path is configured' do
|
describe "when cdn path is configured" do
|
||||||
before do
|
before do
|
||||||
GlobalSetting.expects(:cdn_url)
|
GlobalSetting.expects(:cdn_url).returns("https://some.localcdn.com").at_least_once
|
||||||
.returns('https://some.localcdn.com')
|
|
||||||
.at_least_once
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return the right URL' do
|
it "should return the right URL" do
|
||||||
expect(helper.logo_url).to eq(
|
expect(helper.logo_url).to eq(
|
||||||
"https://some.localcdn.com/#{upload_path}/original/1X/somesha1.png"
|
"https://some.localcdn.com/#{upload_path}/original/1X/somesha1.png",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when logo is an SVG' do
|
describe "when logo is an SVG" do
|
||||||
let(:upload) { Fabricate(:upload, extension: "svg") }
|
let(:upload) { Fabricate(:upload, extension: "svg") }
|
||||||
|
|
||||||
it 'should return nil' do
|
it "should return nil" do
|
||||||
expect(helper.logo_url).to eq(nil)
|
expect(helper.logo_url).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 's3 store' do
|
describe "s3 store" do
|
||||||
let(:upload) { Fabricate(:upload_s3, sha1: "somesha1") }
|
let(:upload) { Fabricate(:upload_s3, sha1: "somesha1") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -165,32 +161,27 @@ RSpec.describe UserNotificationsHelper do
|
||||||
SiteSetting.logo = upload
|
SiteSetting.logo = upload
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return the right URL' do
|
it "should return the right URL" do
|
||||||
expect(helper.logo_url).to eq(
|
expect(helper.logo_url).to eq(
|
||||||
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png"
|
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when global cdn path is configured' do
|
describe "when global cdn path is configured" do
|
||||||
it 'should return the right url' do
|
it "should return the right url" do
|
||||||
GlobalSetting.stubs(:cdn_url).returns('https://some.cdn.com/cluster')
|
GlobalSetting.stubs(:cdn_url).returns("https://some.cdn.com/cluster")
|
||||||
|
|
||||||
expect(helper.logo_url).to eq(
|
expect(helper.logo_url).to eq(
|
||||||
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png"
|
"http://s3-upload-bucket.s3.dualstack.#{SiteSetting.s3_region}.amazonaws.com/original/1X/somesha1.png",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when cdn path is configured' do
|
describe "when cdn path is configured" do
|
||||||
before do
|
before { SiteSetting.s3_cdn_url = "https://some.cdn.com" }
|
||||||
SiteSetting.s3_cdn_url = 'https://some.cdn.com'
|
|
||||||
|
|
||||||
end
|
it "should return the right url" do
|
||||||
|
expect(helper.logo_url).to eq("https://some.cdn.com/original/1X/somesha1.png")
|
||||||
it 'should return the right url' do
|
|
||||||
expect(helper.logo_url).to eq(
|
|
||||||
"https://some.cdn.com/original/1X/somesha1.png"
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,26 +3,23 @@
|
||||||
require "import_export"
|
require "import_export"
|
||||||
|
|
||||||
RSpec.describe ImportExport::CategoryExporter do
|
RSpec.describe ImportExport::CategoryExporter do
|
||||||
|
|
||||||
fab!(:category) { Fabricate(:category) }
|
fab!(:category) { Fabricate(:category) }
|
||||||
fab!(:group) { Fabricate(:group) }
|
fab!(:group) { Fabricate(:group) }
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
fab!(:user2) { Fabricate(:user) }
|
fab!(:user2) { Fabricate(:user) }
|
||||||
fab!(:user3) { Fabricate(:user) }
|
fab!(:user3) { Fabricate(:user) }
|
||||||
|
|
||||||
before do
|
before { STDOUT.stubs(:write) }
|
||||||
STDOUT.stubs(:write)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '.perform' do
|
describe ".perform" do
|
||||||
it 'export the category when it is found' do
|
it "export the category when it is found" do
|
||||||
data = ImportExport::CategoryExporter.new([category.id]).perform.export_data
|
data = ImportExport::CategoryExporter.new([category.id]).perform.export_data
|
||||||
|
|
||||||
expect(data[:categories].count).to eq(1)
|
expect(data[:categories].count).to eq(1)
|
||||||
expect(data[:groups].count).to eq(0)
|
expect(data[:groups].count).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'export the category with permission groups' do
|
it "export the category with permission groups" do
|
||||||
_category_group = Fabricate(:category_group, category: category, group: group)
|
_category_group = Fabricate(:category_group, category: category, group: group)
|
||||||
data = ImportExport::CategoryExporter.new([category.id]).perform.export_data
|
data = ImportExport::CategoryExporter.new([category.id]).perform.export_data
|
||||||
|
|
||||||
|
@ -30,7 +27,7 @@ RSpec.describe ImportExport::CategoryExporter do
|
||||||
expect(data[:groups].count).to eq(1)
|
expect(data[:groups].count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'export multiple categories' do
|
it "export multiple categories" do
|
||||||
category2 = Fabricate(:category)
|
category2 = Fabricate(:category)
|
||||||
_category_group = Fabricate(:category_group, category: category, group: group)
|
_category_group = Fabricate(:category_group, category: category, group: group)
|
||||||
data = ImportExport::CategoryExporter.new([category.id, category2.id]).perform.export_data
|
data = ImportExport::CategoryExporter.new([category.id, category2.id]).perform.export_data
|
||||||
|
@ -39,7 +36,7 @@ RSpec.describe ImportExport::CategoryExporter do
|
||||||
expect(data[:groups].count).to eq(1)
|
expect(data[:groups].count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'export the category with topics and users' do
|
it "export the category with topics and users" do
|
||||||
topic1 = Fabricate(:topic, category: category, user_id: -1)
|
topic1 = Fabricate(:topic, category: category, user_id: -1)
|
||||||
Fabricate(:post, topic: topic1, user: User.find(-1), post_number: 1)
|
Fabricate(:post, topic: topic1, user: User.find(-1), post_number: 1)
|
||||||
topic2 = Fabricate(:topic, category: category, user: user)
|
topic2 = Fabricate(:topic, category: category, user: user)
|
||||||
|
@ -54,5 +51,4 @@ RSpec.describe ImportExport::CategoryExporter do
|
||||||
expect(data[:users].map { |u| u[:id] }).to match_array([user.id, user2.id, user3.id])
|
expect(data[:users].map { |u| u[:id] }).to match_array([user.id, user2.id, user3.id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,12 +3,9 @@
|
||||||
require "import_export/category_structure_exporter"
|
require "import_export/category_structure_exporter"
|
||||||
|
|
||||||
RSpec.describe ImportExport::CategoryStructureExporter do
|
RSpec.describe ImportExport::CategoryStructureExporter do
|
||||||
|
before { STDOUT.stubs(:write) }
|
||||||
|
|
||||||
before do
|
it "export all the categories" do
|
||||||
STDOUT.stubs(:write)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'export all the categories' do
|
|
||||||
category = Fabricate(:category)
|
category = Fabricate(:category)
|
||||||
data = ImportExport::CategoryStructureExporter.new.perform.export_data
|
data = ImportExport::CategoryStructureExporter.new.perform.export_data
|
||||||
|
|
||||||
|
@ -17,7 +14,7 @@ RSpec.describe ImportExport::CategoryStructureExporter do
|
||||||
expect(data[:users].blank?).to eq(true)
|
expect(data[:users].blank?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'export all the categories with permission groups' do
|
it "export all the categories with permission groups" do
|
||||||
category = Fabricate(:category)
|
category = Fabricate(:category)
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
category_group = Fabricate(:category_group, category: category, group: group)
|
category_group = Fabricate(:category_group, category: category, group: group)
|
||||||
|
@ -28,7 +25,7 @@ RSpec.describe ImportExport::CategoryStructureExporter do
|
||||||
expect(data[:users].blank?).to eq(true)
|
expect(data[:users].blank?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'export all the categories with permission groups and users' do
|
it "export all the categories with permission groups and users" do
|
||||||
category = Fabricate(:category)
|
category = Fabricate(:category)
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
user = Fabricate(:user)
|
user = Fabricate(:user)
|
||||||
|
@ -40,5 +37,4 @@ RSpec.describe ImportExport::CategoryStructureExporter do
|
||||||
expect(data[:groups].count).to eq(1)
|
expect(data[:groups].count).to eq(1)
|
||||||
expect(data[:users].count).to eq(1)
|
expect(data[:users].count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,12 +3,9 @@
|
||||||
require "import_export/group_exporter"
|
require "import_export/group_exporter"
|
||||||
|
|
||||||
RSpec.describe ImportExport::GroupExporter do
|
RSpec.describe ImportExport::GroupExporter do
|
||||||
|
before { STDOUT.stubs(:write) }
|
||||||
|
|
||||||
before do
|
it "exports all the groups" do
|
||||||
STDOUT.stubs(:write)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'exports all the groups' do
|
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
user = Fabricate(:user)
|
user = Fabricate(:user)
|
||||||
group_user = Fabricate(:group_user, group: group, user: user)
|
group_user = Fabricate(:group_user, group: group, user: user)
|
||||||
|
@ -18,7 +15,7 @@ RSpec.describe ImportExport::GroupExporter do
|
||||||
expect(data[:users].blank?).to eq(true)
|
expect(data[:users].blank?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'exports all the groups with users' do
|
it "exports all the groups with users" do
|
||||||
group = Fabricate(:group)
|
group = Fabricate(:group)
|
||||||
user = Fabricate(:user)
|
user = Fabricate(:user)
|
||||||
group_user = Fabricate(:group_user, group: group, user: user)
|
group_user = Fabricate(:group_user, group: group, user: user)
|
||||||
|
@ -27,5 +24,4 @@ RSpec.describe ImportExport::GroupExporter do
|
||||||
expect(data[:groups].map { |g| g[:id] }).to include(group.id)
|
expect(data[:groups].map { |g| g[:id] }).to include(group.id)
|
||||||
expect(data[:users].map { |u| u[:id] }).to include(user.id)
|
expect(data[:users].map { |u| u[:id] }).to include(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
require "import_export"
|
require "import_export"
|
||||||
|
|
||||||
RSpec.describe ImportExport::Importer do
|
RSpec.describe ImportExport::Importer do
|
||||||
before do
|
before { STDOUT.stubs(:write) }
|
||||||
STDOUT.stubs(:write)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:import_data) do
|
let(:import_data) do
|
||||||
import_file = Rack::Test::UploadedFile.new(file_from_fixtures("import-export.json", "json"))
|
import_file = Rack::Test::UploadedFile.new(file_from_fixtures("import-export.json", "json"))
|
||||||
|
@ -16,93 +14,79 @@ RSpec.describe ImportExport::Importer do
|
||||||
ImportExport::Importer.new(data).perform
|
ImportExport::Importer.new(data).perform
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.perform' do
|
describe ".perform" do
|
||||||
it 'topics and users' do
|
it "topics and users" do
|
||||||
data = import_data.dup
|
data = import_data.dup
|
||||||
data[:categories] = nil
|
data[:categories] = nil
|
||||||
data[:groups] = nil
|
data[:groups] = nil
|
||||||
|
|
||||||
expect {
|
expect { import(data) }.to not_change { Category.count }.and not_change {
|
||||||
import(data)
|
Group.count
|
||||||
}.to not_change { Category.count }
|
}.and change { Topic.count }.by(2).and change { User.count }.by(2)
|
||||||
.and not_change { Group.count }
|
|
||||||
.and change { Topic.count }.by(2)
|
|
||||||
.and change { User.count }.by(2)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with categories and groups' do
|
context "with categories and groups" do
|
||||||
it 'works' do
|
it "works" do
|
||||||
data = import_data.dup
|
data = import_data.dup
|
||||||
data[:topics] = nil
|
data[:topics] = nil
|
||||||
data[:users] = nil
|
data[:users] = nil
|
||||||
|
|
||||||
expect {
|
expect { import(data) }.to change { Category.count }.by(6).and change { Group.count }.by(
|
||||||
import(data)
|
2,
|
||||||
}.to change { Category.count }.by(6)
|
).and change { Topic.count }.by(6).and not_change { User.count }
|
||||||
.and change { Group.count }.by(2)
|
|
||||||
.and change { Topic.count }.by(6)
|
|
||||||
.and not_change { User.count }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'works with sub-sub-categories' do
|
it "works with sub-sub-categories" do
|
||||||
data = import_data.dup
|
data = import_data.dup
|
||||||
|
|
||||||
# 11 -> 10 -> 15
|
# 11 -> 10 -> 15
|
||||||
data[:categories].find { |c| c[:id] == 10 }[:parent_category_id] = 11
|
data[:categories].find { |c| c[:id] == 10 }[:parent_category_id] = 11
|
||||||
data[:categories].find { |c| c[:id] == 15 }[:parent_category_id] = 10
|
data[:categories].find { |c| c[:id] == 15 }[:parent_category_id] = 10
|
||||||
|
|
||||||
expect { import(data) }
|
expect { import(data) }.to change { Category.count }.by(6).and change {
|
||||||
.to change { Category.count }.by(6)
|
SiteSetting.max_category_nesting
|
||||||
.and change { SiteSetting.max_category_nesting }.from(2).to(3)
|
}.from(2).to(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'fixes permissions' do
|
it "fixes permissions" do
|
||||||
data = import_data.dup
|
data = import_data.dup
|
||||||
data[:categories].find { |c| c[:id] == 10 }[:permissions_params] = { custom_group: 1 }
|
data[:categories].find { |c| c[:id] == 10 }[:permissions_params] = { custom_group: 1 }
|
||||||
data[:categories].find { |c| c[:id] == 15 }[:permissions_params] = { staff: 1 }
|
data[:categories].find { |c| c[:id] == 15 }[:permissions_params] = { staff: 1 }
|
||||||
|
|
||||||
permissions = data[:categories].find { |c| c[:id] == 10 }[:permissions_params]
|
permissions = data[:categories].find { |c| c[:id] == 10 }[:permissions_params]
|
||||||
|
|
||||||
expect { import(data) }
|
expect { import(data) }.to change { Category.count }.by(6).and change {
|
||||||
.to change { Category.count }.by(6)
|
permissions[:staff]
|
||||||
.and change { permissions[:staff] }.from(nil).to(1)
|
}.from(nil).to(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'categories, groups and users' do
|
it "categories, groups and users" do
|
||||||
data = import_data.dup
|
data = import_data.dup
|
||||||
data[:topics] = nil
|
data[:topics] = nil
|
||||||
|
|
||||||
expect {
|
expect { import(data) }.to change { Category.count }.by(6).and change { Group.count }.by(
|
||||||
import(data)
|
2,
|
||||||
}.to change { Category.count }.by(6)
|
).and change { Topic.count }.by(6).and change { User.count }.by(2)
|
||||||
.and change { Group.count }.by(2)
|
|
||||||
.and change { Topic.count }.by(6)
|
|
||||||
.and change { User.count }.by(2)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'groups' do
|
it "groups" do
|
||||||
data = import_data.dup
|
data = import_data.dup
|
||||||
data[:categories] = nil
|
data[:categories] = nil
|
||||||
data[:topics] = nil
|
data[:topics] = nil
|
||||||
data[:users] = nil
|
data[:users] = nil
|
||||||
|
|
||||||
expect {
|
expect { import(data) }.to not_change { Category.count }.and change { Group.count }.by(
|
||||||
import(data)
|
2,
|
||||||
}.to not_change { Category.count }
|
).and not_change { Topic.count }.and not_change { User.count }
|
||||||
.and change { Group.count }.by(2)
|
|
||||||
.and not_change { Topic.count }
|
|
||||||
.and not_change { User.count }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'all' do
|
it "all" do
|
||||||
expect {
|
expect { import(import_data) }.to change { Category.count }.by(6).and change {
|
||||||
import(import_data)
|
Group.count
|
||||||
}.to change { Category.count }.by(6)
|
}.by(2).and change { Topic.count }.by(8).and change { User.count }.by(2).and change {
|
||||||
.and change { Group.count }.by(2)
|
TranslationOverride.count
|
||||||
.and change { Topic.count }.by(8)
|
}.by(1)
|
||||||
.and change { User.count }.by(2)
|
|
||||||
.and change { TranslationOverride.count }.by(1)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,17 +3,14 @@
|
||||||
require "import_export"
|
require "import_export"
|
||||||
|
|
||||||
RSpec.describe ImportExport::TopicExporter do
|
RSpec.describe ImportExport::TopicExporter do
|
||||||
|
before { STDOUT.stubs(:write) }
|
||||||
before do
|
|
||||||
STDOUT.stubs(:write)
|
|
||||||
end
|
|
||||||
|
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
fab!(:topic) { Fabricate(:topic, user: user) }
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
||||||
fab!(:post) { Fabricate(:post, topic: topic, user: user) }
|
fab!(:post) { Fabricate(:post, topic: topic, user: user) }
|
||||||
|
|
||||||
describe '.perform' do
|
describe ".perform" do
|
||||||
it 'export a single topic' do
|
it "export a single topic" do
|
||||||
data = ImportExport::TopicExporter.new([topic.id]).perform.export_data
|
data = ImportExport::TopicExporter.new([topic.id]).perform.export_data
|
||||||
|
|
||||||
expect(data[:categories].blank?).to eq(true)
|
expect(data[:categories].blank?).to eq(true)
|
||||||
|
@ -22,7 +19,7 @@ RSpec.describe ImportExport::TopicExporter do
|
||||||
expect(data[:users].count).to eq(1)
|
expect(data[:users].count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'export multiple topics' do
|
it "export multiple topics" do
|
||||||
topic2 = Fabricate(:topic, user: user)
|
topic2 = Fabricate(:topic, user: user)
|
||||||
_post2 = Fabricate(:post, user: user, topic: topic2)
|
_post2 = Fabricate(:post, user: user, topic: topic2)
|
||||||
data = ImportExport::TopicExporter.new([topic.id, topic2.id]).perform.export_data
|
data = ImportExport::TopicExporter.new([topic.id, topic2.id]).perform.export_data
|
||||||
|
@ -33,5 +30,4 @@ RSpec.describe ImportExport::TopicExporter do
|
||||||
expect(data[:users].map { |u| u[:id] }).to match_array([user.id])
|
expect(data[:users].map { |u| u[:id] }).to match_array([user.id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'Setting changes' do
|
RSpec.describe "Setting changes" do
|
||||||
describe '#must_approve_users' do
|
describe "#must_approve_users" do
|
||||||
before { SiteSetting.must_approve_users = false }
|
before { SiteSetting.must_approve_users = false }
|
||||||
|
|
||||||
it 'does not approve a user with associated reviewables' do
|
it "does not approve a user with associated reviewables" do
|
||||||
user_pending_approval = Fabricate(:reviewable_user).target
|
user_pending_approval = Fabricate(:reviewable_user).target
|
||||||
|
|
||||||
SiteSetting.must_approve_users = true
|
SiteSetting.must_approve_users = true
|
||||||
|
@ -12,7 +12,7 @@ RSpec.describe 'Setting changes' do
|
||||||
expect(user_pending_approval.reload.approved?).to eq(false)
|
expect(user_pending_approval.reload.approved?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'approves a user with no associated reviewables' do
|
it "approves a user with no associated reviewables" do
|
||||||
non_approved_user = Fabricate(:user, approved: false)
|
non_approved_user = Fabricate(:user, approved: false)
|
||||||
|
|
||||||
SiteSetting.must_approve_users = true
|
SiteSetting.must_approve_users = true
|
||||||
|
@ -21,10 +21,10 @@ RSpec.describe 'Setting changes' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#reviewable_low_priority_threshold' do
|
describe "#reviewable_low_priority_threshold" do
|
||||||
let(:new_threshold) { 5 }
|
let(:new_threshold) { 5 }
|
||||||
|
|
||||||
it 'sets the low priority value' do
|
it "sets the low priority value" do
|
||||||
medium_threshold = 10
|
medium_threshold = 10
|
||||||
Reviewable.set_priorities(medium: medium_threshold)
|
Reviewable.set_priorities(medium: medium_threshold)
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,21 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'api keys' do
|
RSpec.describe "api keys" do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:api_key) { ApiKey.create!(user_id: user.id, created_by_id: Discourse.system_user) }
|
let(:api_key) { ApiKey.create!(user_id: user.id, created_by_id: Discourse.system_user) }
|
||||||
|
|
||||||
it 'works in headers' do
|
it "works in headers" do
|
||||||
get '/session/current.json', headers: {
|
get "/session/current.json", headers: { HTTP_API_KEY: api_key.key }
|
||||||
HTTP_API_KEY: api_key.key
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(response.parsed_body["current_user"]["username"]).to eq(user.username)
|
expect(response.parsed_body["current_user"]["username"]).to eq(user.username)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not work in parameters' do
|
it "does not work in parameters" do
|
||||||
get '/session/current.json', params: {
|
get "/session/current.json", params: { api_key: api_key.key }
|
||||||
api_key: api_key.key
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows parameters on ics routes' do
|
it "allows parameters on ics routes" do
|
||||||
get "/u/#{user.username}/bookmarks.ics?api_key=#{api_key.key}&api_username=#{user.username.downcase}"
|
get "/u/#{user.username}/bookmarks.ics?api_key=#{api_key.key}&api_username=#{user.username.downcase}"
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
@ -28,14 +24,14 @@ RSpec.describe 'api keys' do
|
||||||
expect(response.status).to eq(403)
|
expect(response.status).to eq(403)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows parameters for handle mail' do
|
it "allows parameters for handle mail" do
|
||||||
admin_api_key = ApiKey.create!(user: Fabricate(:admin), created_by_id: Discourse.system_user)
|
admin_api_key = ApiKey.create!(user: Fabricate(:admin), created_by_id: Discourse.system_user)
|
||||||
|
|
||||||
post "/admin/email/handle_mail.json?api_key=#{admin_api_key.key}", params: { email: "blah" }
|
post "/admin/email/handle_mail.json?api_key=#{admin_api_key.key}", params: { email: "blah" }
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows parameters for rss feeds' do
|
it "allows parameters for rss feeds" do
|
||||||
SiteSetting.login_required = true
|
SiteSetting.login_required = true
|
||||||
|
|
||||||
get "/latest.rss?api_key=#{api_key.key}&api_username=#{user.username.downcase}"
|
get "/latest.rss?api_key=#{api_key.key}&api_username=#{user.username.downcase}"
|
||||||
|
@ -52,32 +48,28 @@ RSpec.describe 'api keys' do
|
||||||
plugin.add_api_parameter_route methods: [:get], actions: ["session#current"]
|
plugin.add_api_parameter_route methods: [:get], actions: ["session#current"]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows parameter access to the registered route' do
|
it "allows parameter access to the registered route" do
|
||||||
get '/session/current.json', params: {
|
get "/session/current.json", params: { api_key: api_key.key }
|
||||||
api_key: api_key.key
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
RSpec.describe 'user api keys' do
|
RSpec.describe "user api keys" do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:user_api_key) { Fabricate(:readonly_user_api_key, user: user) }
|
let(:user_api_key) { Fabricate(:readonly_user_api_key, user: user) }
|
||||||
|
|
||||||
it 'updates last used time on use' do
|
it "updates last used time on use" do
|
||||||
freeze_time
|
freeze_time
|
||||||
|
|
||||||
user_api_key.update_columns(last_used_at: 7.days.ago)
|
user_api_key.update_columns(last_used_at: 7.days.ago)
|
||||||
|
|
||||||
get '/session/current.json', headers: {
|
get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key }
|
||||||
HTTP_USER_API_KEY: user_api_key.key,
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(user_api_key.reload.last_used_at).to eq_time(Time.zone.now)
|
expect(user_api_key.reload.last_used_at).to eq_time(Time.zone.now)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows parameters on ics routes' do
|
it "allows parameters on ics routes" do
|
||||||
get "/u/#{user.username}/bookmarks.ics?user_api_key=#{user_api_key.key}"
|
get "/u/#{user.username}/bookmarks.ics?user_api_key=#{user_api_key.key}"
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
@ -86,7 +78,7 @@ RSpec.describe 'user api keys' do
|
||||||
expect(response.status).to eq(403)
|
expect(response.status).to eq(403)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows parameters for rss feeds' do
|
it "allows parameters for rss feeds" do
|
||||||
SiteSetting.login_required = true
|
SiteSetting.login_required = true
|
||||||
|
|
||||||
get "/latest.rss?user_api_key=#{user_api_key.key}"
|
get "/latest.rss?user_api_key=#{user_api_key.key}"
|
||||||
|
@ -102,27 +94,19 @@ RSpec.describe 'user api keys' do
|
||||||
|
|
||||||
calendar_key = Fabricate(:bookmarks_calendar_user_api_key, user: admin)
|
calendar_key = Fabricate(:bookmarks_calendar_user_api_key, user: admin)
|
||||||
|
|
||||||
get "/u/#{user.username}/bookmarks.json", headers: {
|
get "/u/#{user.username}/bookmarks.json", headers: { HTTP_USER_API_KEY: calendar_key.key }
|
||||||
HTTP_USER_API_KEY: calendar_key.key,
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(403) # Does not allow json
|
expect(response.status).to eq(403) # Does not allow json
|
||||||
|
|
||||||
get "/u/#{user.username}/bookmarks.ics", headers: {
|
get "/u/#{user.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key }
|
||||||
HTTP_USER_API_KEY: calendar_key.key,
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(200) # Allows ICS
|
expect(response.status).to eq(200) # Allows ICS
|
||||||
|
|
||||||
# Now restrict the key
|
# Now restrict the key
|
||||||
calendar_key.scopes.first.update(allowed_parameters: { username: admin.username })
|
calendar_key.scopes.first.update(allowed_parameters: { username: admin.username })
|
||||||
|
|
||||||
get "/u/#{user.username}/bookmarks.ics", headers: {
|
get "/u/#{user.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key }
|
||||||
HTTP_USER_API_KEY: calendar_key.key,
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(403) # Cannot access another users calendar
|
expect(response.status).to eq(403) # Cannot access another users calendar
|
||||||
|
|
||||||
get "/u/#{admin.username}/bookmarks.ics", headers: {
|
get "/u/#{admin.username}/bookmarks.ics", headers: { HTTP_USER_API_KEY: calendar_key.key }
|
||||||
HTTP_USER_API_KEY: calendar_key.key,
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(200) # Can access own calendar
|
expect(response.status).to eq(200) # Can access own calendar
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -138,12 +122,9 @@ RSpec.describe 'user api keys' do
|
||||||
user_api_key.save!
|
user_api_key.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows parameter access to the registered route' do
|
it "allows parameter access to the registered route" do
|
||||||
get '/session/current.json', headers: {
|
get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key }
|
||||||
HTTP_USER_API_KEY: user_api_key.key
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,9 +12,7 @@ RSpec.describe "auto reject reviewable users" do
|
||||||
Jobs::AutoQueueHandler.new.execute({})
|
Jobs::AutoQueueHandler.new.execute({})
|
||||||
|
|
||||||
expect(old_user.reload.rejected?).to eq(true)
|
expect(old_user.reload.rejected?).to eq(true)
|
||||||
expect(UserHistory.last.context).to eq(
|
expect(UserHistory.last.context).to eq(I18n.t("user.destroy_reasons.reviewable_reject_auto"))
|
||||||
I18n.t("user.destroy_reasons.reviewable_reject_auto")
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,11 +3,20 @@
|
||||||
RSpec.describe "hotlinked media blocking" do
|
RSpec.describe "hotlinked media blocking" do
|
||||||
let(:hotlinked_url) { "http://example.com/images/2/2e/Longcat1.png" }
|
let(:hotlinked_url) { "http://example.com/images/2/2e/Longcat1.png" }
|
||||||
let(:onebox_url) { "http://example.com/onebox" }
|
let(:onebox_url) { "http://example.com/onebox" }
|
||||||
let(:png) { Base64.decode64("R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==") }
|
let(:png) do
|
||||||
|
Base64.decode64(
|
||||||
|
"R0lGODlhAQABALMAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwICAgP8AAAD/AP//AAAA//8A/wD//wBiZCH5BAEAAA8ALAAAAAABAAEAAAQC8EUAOw==",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.download_remote_images_to_local = false
|
SiteSetting.download_remote_images_to_local = false
|
||||||
stub_request(:get, hotlinked_url).to_return(body: png, headers: { "Content-Type" => "image/png" })
|
stub_request(:get, hotlinked_url).to_return(
|
||||||
|
body: png,
|
||||||
|
headers: {
|
||||||
|
"Content-Type" => "image/png",
|
||||||
|
},
|
||||||
|
)
|
||||||
stub_image_size
|
stub_image_size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -19,38 +28,65 @@ RSpec.describe "hotlinked media blocking" do
|
||||||
context "with hotlinked media blocked, before post-processing" do
|
context "with hotlinked media blocked, before post-processing" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.block_hotlinked_media = true
|
SiteSetting.block_hotlinked_media = true
|
||||||
Oneboxer.stubs(:cached_onebox).returns("<aside class='onebox'><img src='#{hotlinked_url}'></aside>")
|
Oneboxer.stubs(:cached_onebox).returns(
|
||||||
|
"<aside class='onebox'><img src='#{hotlinked_url}'></aside>",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "blocks hotlinked images" do
|
it "blocks hotlinked images" do
|
||||||
post = Fabricate(:post, raw: "<img src='#{hotlinked_url}'>")
|
post = Fabricate(:post, raw: "<img src='#{hotlinked_url}'>")
|
||||||
expect(post.cooked).not_to have_tag("img[src]")
|
expect(post.cooked).not_to have_tag("img[src]")
|
||||||
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
|
expect(post.cooked).to have_tag(
|
||||||
|
"img",
|
||||||
|
with: {
|
||||||
|
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "blocks hotlinked videos with src" do
|
it "blocks hotlinked videos with src" do
|
||||||
post = Fabricate(:post, raw: "![alt text|video](#{hotlinked_url})")
|
post = Fabricate(:post, raw: "![alt text|video](#{hotlinked_url})")
|
||||||
expect(post.cooked).not_to have_tag("video source[src]")
|
expect(post.cooked).not_to have_tag("video source[src]")
|
||||||
expect(post.cooked).to have_tag("video source", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
|
expect(post.cooked).to have_tag(
|
||||||
|
"video source",
|
||||||
|
with: {
|
||||||
|
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "blocks hotlinked videos with srcset" do
|
it "blocks hotlinked videos with srcset" do
|
||||||
srcset = "#{hotlinked_url} 1x,https://example.com 2x"
|
srcset = "#{hotlinked_url} 1x,https://example.com 2x"
|
||||||
post = Fabricate(:post, raw: "<video><source srcset='#{srcset}'></video>")
|
post = Fabricate(:post, raw: "<video><source srcset='#{srcset}'></video>")
|
||||||
expect(post.cooked).not_to have_tag("video source[srcset]")
|
expect(post.cooked).not_to have_tag("video source[srcset]")
|
||||||
expect(post.cooked).to have_tag("video source", with: { PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR => srcset })
|
expect(post.cooked).to have_tag(
|
||||||
|
"video source",
|
||||||
|
with: {
|
||||||
|
PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR => srcset,
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "blocks hotlinked audio" do
|
it "blocks hotlinked audio" do
|
||||||
post = Fabricate(:post, raw: "![alt text|audio](#{hotlinked_url})")
|
post = Fabricate(:post, raw: "![alt text|audio](#{hotlinked_url})")
|
||||||
expect(post.cooked).not_to have_tag("audio source[src]")
|
expect(post.cooked).not_to have_tag("audio source[src]")
|
||||||
expect(post.cooked).to have_tag("audio source", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
|
expect(post.cooked).to have_tag(
|
||||||
|
"audio source",
|
||||||
|
with: {
|
||||||
|
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "blocks hotlinked onebox content when cached (post_analyzer)" do
|
it "blocks hotlinked onebox content when cached (post_analyzer)" do
|
||||||
post = Fabricate(:post, raw: "#{onebox_url}")
|
post = Fabricate(:post, raw: "#{onebox_url}")
|
||||||
expect(post.cooked).not_to have_tag("img[src]")
|
expect(post.cooked).not_to have_tag("img[src]")
|
||||||
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url })
|
expect(post.cooked).to have_tag(
|
||||||
|
"img",
|
||||||
|
with: {
|
||||||
|
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => hotlinked_url,
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "allows relative URLs" do
|
it "allows relative URLs" do
|
||||||
|
@ -81,8 +117,18 @@ RSpec.describe "hotlinked media blocking" do
|
||||||
post.reload
|
post.reload
|
||||||
expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com" })
|
expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com" })
|
||||||
expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com/myimage.png" })
|
expect(post.cooked).to have_tag("img", with: { "src" => "https://example.com/myimage.png" })
|
||||||
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://example.com.malicious.com/myimage.png" })
|
expect(post.cooked).to have_tag(
|
||||||
expect(post.cooked).to have_tag("img", with: { PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://malicious.invalid/https://example.com" })
|
"img",
|
||||||
|
with: {
|
||||||
|
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://example.com.malicious.com/myimage.png",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect(post.cooked).to have_tag(
|
||||||
|
"img",
|
||||||
|
with: {
|
||||||
|
PrettyText::BLOCKED_HOTLINKED_SRC_ATTR => "https://malicious.invalid/https://example.com",
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "allows multiple exceptions" do
|
it "allows multiple exceptions" do
|
||||||
|
@ -126,7 +172,7 @@ RSpec.describe "hotlinked media blocking" do
|
||||||
expect(post.cooked).not_to have_tag("audio")
|
expect(post.cooked).not_to have_tag("audio")
|
||||||
expect(post.cooked).to have_tag(
|
expect(post.cooked).to have_tag(
|
||||||
"a.blocked-hotlinked-placeholder[href^='http://example.com'][rel='noopener nofollow ugc']",
|
"a.blocked-hotlinked-placeholder[href^='http://example.com'][rel='noopener nofollow ugc']",
|
||||||
count: 4
|
count: 4,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,13 +6,13 @@ RSpec.describe "category tag restrictions" do
|
||||||
DiscourseTagging.filter_allowed_tags(Guardian.new(user), opts)
|
DiscourseTagging.filter_allowed_tags(Guardian.new(user), opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
fab!(:tag1) { Fabricate(:tag, name: 'tag1') }
|
fab!(:tag1) { Fabricate(:tag, name: "tag1") }
|
||||||
fab!(:tag2) { Fabricate(:tag, name: 'tag2') }
|
fab!(:tag2) { Fabricate(:tag, name: "tag2") }
|
||||||
fab!(:tag3) { Fabricate(:tag, name: 'tag3') }
|
fab!(:tag3) { Fabricate(:tag, name: "tag3") }
|
||||||
fab!(:tag4) { Fabricate(:tag, name: 'tag4') }
|
fab!(:tag4) { Fabricate(:tag, name: "tag4") }
|
||||||
let(:tag_with_colon) { Fabricate(:tag, name: 'with:colon') }
|
let(:tag_with_colon) { Fabricate(:tag, name: "with:colon") }
|
||||||
|
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
fab!(:admin) { Fabricate(:admin) }
|
fab!(:admin) { Fabricate(:admin) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -23,29 +23,29 @@ RSpec.describe "category tag restrictions" do
|
||||||
|
|
||||||
context "with tags restricted to one category" do
|
context "with tags restricted to one category" do
|
||||||
fab!(:category_with_tags) { Fabricate(:category) }
|
fab!(:category_with_tags) { Fabricate(:category) }
|
||||||
fab!(:other_category) { Fabricate(:category) }
|
fab!(:other_category) { Fabricate(:category) }
|
||||||
|
|
||||||
before do
|
before { category_with_tags.tags = [tag1, tag2] }
|
||||||
category_with_tags.tags = [tag1, tag2]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "tags belonging to that category can only be used there" do
|
it "tags belonging to that category can only be used there" do
|
||||||
msg = I18n.t(
|
msg =
|
||||||
"tags.forbidden.category_does_not_allow_tags",
|
I18n.t(
|
||||||
count: 1,
|
"tags.forbidden.category_does_not_allow_tags",
|
||||||
tags: tag3.name,
|
count: 1,
|
||||||
category: category_with_tags.name
|
tags: tag3.name,
|
||||||
)
|
category: category_with_tags.name,
|
||||||
|
)
|
||||||
expect {
|
expect {
|
||||||
create_post(category: category_with_tags, tags: [tag1.name, tag2.name, tag3.name])
|
create_post(category: category_with_tags, tags: [tag1.name, tag2.name, tag3.name])
|
||||||
}.to raise_error(StandardError, msg)
|
}.to raise_error(StandardError, msg)
|
||||||
|
|
||||||
msg = I18n.t(
|
msg =
|
||||||
"tags.forbidden.restricted_tags_cannot_be_used_in_category",
|
I18n.t(
|
||||||
count: 2,
|
"tags.forbidden.restricted_tags_cannot_be_used_in_category",
|
||||||
tags: [tag1, tag2].map(&:name).sort.join(", "),
|
count: 2,
|
||||||
category: other_category.name
|
tags: [tag1, tag2].map(&:name).sort.join(", "),
|
||||||
)
|
category: other_category.name,
|
||||||
|
)
|
||||||
expect {
|
expect {
|
||||||
create_post(category: other_category, tags: [tag1.name, tag2.name, tag3.name])
|
create_post(category: other_category, tags: [tag1.name, tag2.name, tag3.name])
|
||||||
}.to raise_error(StandardError, msg)
|
}.to raise_error(StandardError, msg)
|
||||||
|
@ -53,27 +53,60 @@ RSpec.describe "category tag restrictions" do
|
||||||
|
|
||||||
it "search can show only permitted tags" do
|
it "search can show only permitted tags" do
|
||||||
expect(filter_allowed_tags.count).to eq(Tag.count)
|
expect(filter_allowed_tags.count).to eq(Tag.count)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag2])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category_with_tags),
|
||||||
|
[tag1, tag2],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name], term: 'tag'), [tag2])
|
filter_allowed_tags(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
|
for_input: true,
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4])
|
category: category_with_tags,
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4])
|
selected_tags: [tag1.name],
|
||||||
|
),
|
||||||
|
[tag2],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: category_with_tags,
|
||||||
|
selected_tags: [tag1.name],
|
||||||
|
term: "tag",
|
||||||
|
),
|
||||||
|
[tag2],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: other_category,
|
||||||
|
selected_tags: [tag3.name],
|
||||||
|
term: "tag",
|
||||||
|
),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "search can handle colons in tag names" do
|
it "search can handle colons in tag names" do
|
||||||
tag_with_colon
|
tag_with_colon
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, term: 'with:c'), [tag_with_colon])
|
expect_same_tag_names(filter_allowed_tags(for_input: true, term: "with:c"), [tag_with_colon])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can't create new tags in a restricted category" do
|
it "can't create new tags in a restricted category" do
|
||||||
msg = I18n.t(
|
msg =
|
||||||
"tags.forbidden.category_does_not_allow_tags",
|
I18n.t(
|
||||||
count: 1,
|
"tags.forbidden.category_does_not_allow_tags",
|
||||||
tags: "newtag",
|
count: 1,
|
||||||
category: category_with_tags.name
|
tags: "newtag",
|
||||||
)
|
category: category_with_tags.name,
|
||||||
|
)
|
||||||
expect {
|
expect {
|
||||||
create_post(category: category_with_tags, tags: [tag1.name, "newtag"])
|
create_post(category: category_with_tags, tags: [tag1.name, "newtag"])
|
||||||
}.to raise_error(StandardError, msg)
|
}.to raise_error(StandardError, msg)
|
||||||
|
@ -89,61 +122,161 @@ RSpec.describe "category tag restrictions" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can create tags when changing category settings" do
|
it "can create tags when changing category settings" do
|
||||||
expect { other_category.update(allowed_tags: ['newtag']) }.to change { Tag.count }.by(1)
|
expect { other_category.update(allowed_tags: ["newtag"]) }.to change { Tag.count }.by(1)
|
||||||
expect { other_category.update(allowed_tags: [tag1.name, 'tag-stuff', tag2.name, 'another-tag']) }.to change { Tag.count }.by(2)
|
expect {
|
||||||
|
other_category.update(allowed_tags: [tag1.name, "tag-stuff", tag2.name, "another-tag"])
|
||||||
|
}.to change { Tag.count }.by(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with required tags from tag group' do
|
context "with required tags from tag group" do
|
||||||
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
||||||
before { category_with_tags.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
|
before do
|
||||||
|
category_with_tags.update!(
|
||||||
|
category_required_tag_groups: [
|
||||||
|
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "search only returns the allowed tags" do
|
it "search only returns the allowed tags" do
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2])
|
filter_allowed_tags(for_input: true, category: category_with_tags),
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag2.name]), [tag1])
|
[tag1],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: category_with_tags,
|
||||||
|
selected_tags: [tag1.name],
|
||||||
|
),
|
||||||
|
[tag2],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: category_with_tags,
|
||||||
|
selected_tags: [tag2.name],
|
||||||
|
),
|
||||||
|
[tag1],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when category allows other tags to be used' do
|
context "when category allows other tags to be used" do
|
||||||
before do
|
before { category_with_tags.update!(allow_global_tags: true) }
|
||||||
category_with_tags.update!(allow_global_tags: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "search can show the permitted tags" do
|
it "search can show the permitted tags" do
|
||||||
expect(filter_allowed_tags.count).to eq(Tag.count)
|
expect(filter_allowed_tags.count).to eq(Tag.count)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category_with_tags),
|
||||||
|
[tag1, tag2, tag3, tag4],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name], term: 'tag'), [tag2, tag3, tag4])
|
filter_allowed_tags(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
|
for_input: true,
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4])
|
category: category_with_tags,
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4])
|
selected_tags: [tag1.name],
|
||||||
|
),
|
||||||
|
[tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: category_with_tags,
|
||||||
|
selected_tags: [tag1.name],
|
||||||
|
term: "tag",
|
||||||
|
),
|
||||||
|
[tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: other_category,
|
||||||
|
selected_tags: [tag3.name],
|
||||||
|
),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: other_category,
|
||||||
|
selected_tags: [tag3.name],
|
||||||
|
term: "tag",
|
||||||
|
),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "works if no tags are restricted to the category" do
|
it "works if no tags are restricted to the category" do
|
||||||
other_category.update!(allow_global_tags: true)
|
other_category.update!(allow_global_tags: true)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name]), [tag4])
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category, selected_tags: [tag3.name], term: 'tag'), [tag4])
|
[tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: other_category,
|
||||||
|
selected_tags: [tag3.name],
|
||||||
|
),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: other_category,
|
||||||
|
selected_tags: [tag3.name],
|
||||||
|
term: "tag",
|
||||||
|
),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with required tags from tag group' do
|
context "with required tags from tag group" do
|
||||||
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
||||||
before { category_with_tags.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
|
before do
|
||||||
|
category_with_tags.update!(
|
||||||
|
category_required_tag_groups: [
|
||||||
|
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "search only returns the allowed tags" do
|
it "search only returns the allowed tags" do
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags), [tag1, tag3])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag1.name]), [tag2, tag3, tag4])
|
filter_allowed_tags(for_input: true, category: category_with_tags),
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category_with_tags, selected_tags: [tag2.name]), [tag1, tag3])
|
[tag1, tag3],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: category_with_tags,
|
||||||
|
selected_tags: [tag1.name],
|
||||||
|
),
|
||||||
|
[tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: category_with_tags,
|
||||||
|
selected_tags: [tag2.name],
|
||||||
|
),
|
||||||
|
[tag1, tag3],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with tag groups restricted to a category" do
|
context "with tag groups restricted to a category" do
|
||||||
fab!(:tag_group1) { Fabricate(:tag_group) }
|
fab!(:tag_group1) { Fabricate(:tag_group) }
|
||||||
fab!(:category) { Fabricate(:category) }
|
fab!(:category) { Fabricate(:category) }
|
||||||
fab!(:other_category) { Fabricate(:category) }
|
fab!(:other_category) { Fabricate(:category) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
tag_group1.tags = [tag1, tag2]
|
tag_group1.tags = [tag1, tag2]
|
||||||
|
@ -156,7 +289,10 @@ RSpec.describe "category tag restrictions" do
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
||||||
|
|
||||||
tag_group1.tags = [tag2, tag3, tag4]
|
tag_group1.tags = [tag2, tag3, tag4]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
|
[tag2, tag3, tag4],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1])
|
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1])
|
||||||
end
|
end
|
||||||
|
@ -165,74 +301,122 @@ RSpec.describe "category tag restrictions" do
|
||||||
category.allowed_tags = [tag4.name]
|
category.allowed_tags = [tag4.name]
|
||||||
category.reload
|
category.reload
|
||||||
|
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
|
[tag1, tag2, tag4],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3])
|
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "enforces restrictions when creating a topic" do
|
it "enforces restrictions when creating a topic" do
|
||||||
msg = I18n.t(
|
msg =
|
||||||
"tags.forbidden.category_does_not_allow_tags",
|
I18n.t(
|
||||||
count: 1,
|
"tags.forbidden.category_does_not_allow_tags",
|
||||||
tags: "newtag",
|
count: 1,
|
||||||
category: category.name
|
tags: "newtag",
|
||||||
|
category: category.name,
|
||||||
|
)
|
||||||
|
expect { create_post(category: category, tags: [tag1.name, "newtag"]) }.to raise_error(
|
||||||
|
StandardError,
|
||||||
|
msg,
|
||||||
)
|
)
|
||||||
expect {
|
|
||||||
create_post(category: category, tags: [tag1.name, "newtag"])
|
|
||||||
}.to raise_error(StandardError, msg)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles colons" do
|
it "handles colons" do
|
||||||
tag_with_colon
|
tag_with_colon
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, term: 'with:c'), [tag_with_colon])
|
expect_same_tag_names(filter_allowed_tags(for_input: true, term: "with:c"), [tag_with_colon])
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with required tags from tag group' do
|
context "with required tags from tag group" do
|
||||||
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
||||||
before { category.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
|
before do
|
||||||
|
category.update!(
|
||||||
|
category_required_tag_groups: [
|
||||||
|
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "search only returns the allowed tags" do
|
it "search only returns the allowed tags" do
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1])
|
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1])
|
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]),
|
||||||
|
[tag2],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]),
|
||||||
|
[tag1],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when category allows other tags to be used' do
|
context "when category allows other tags to be used" do
|
||||||
before do
|
before { category.update!(allow_global_tags: true) }
|
||||||
category.update!(allow_global_tags: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'filters tags correctly' do
|
it "filters tags correctly" do
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
|
[tag1, tag2, tag3, tag4],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag3, tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag3, tag4],
|
||||||
|
)
|
||||||
|
|
||||||
tag_group1.tags = [tag2, tag3, tag4]
|
tag_group1.tags = [tag2, tag3, tag4]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
|
[tag1, tag2, tag3, tag4],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag1],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "works if no tags are restricted to the category" do
|
it "works if no tags are restricted to the category" do
|
||||||
other_category.update!(allow_global_tags: true)
|
other_category.update!(allow_global_tags: true)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag3, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag3, tag4],
|
||||||
|
)
|
||||||
tag_group1.tags = [tag2, tag3, tag4]
|
tag_group1.tags = [tag2, tag3, tag4]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag1],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with required tags from tag group' do
|
context "with required tags from tag group" do
|
||||||
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag3]) }
|
||||||
before { category.update!(category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
|
before do
|
||||||
|
category.update!(
|
||||||
|
category_required_tag_groups: [
|
||||||
|
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "search only returns the allowed tags" do
|
it "search only returns the allowed tags" do
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag3])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2, tag3, tag4])
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1, tag3])
|
[tag1, tag3],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]),
|
||||||
|
[tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]),
|
||||||
|
[tag1, tag3],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when another category has restricted tags using groups' do
|
context "when another category has restricted tags using groups" do
|
||||||
fab!(:category2) { Fabricate(:category) }
|
fab!(:category2) { Fabricate(:category) }
|
||||||
fab!(:tag_group2) { Fabricate(:tag_group) }
|
fab!(:tag_group2) { Fabricate(:tag_group) }
|
||||||
|
|
||||||
|
@ -242,32 +426,59 @@ RSpec.describe "category tag restrictions" do
|
||||||
category2.reload
|
category2.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'filters tags correctly' do
|
it "filters tags correctly" do
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category2),
|
||||||
|
[tag2, tag3],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
|
[tag1, tag2, tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't care about tags in a group that isn't used in a category" do
|
it "doesn't care about tags in a group that isn't used in a category" do
|
||||||
unused_tag_group = Fabricate(:tag_group)
|
unused_tag_group = Fabricate(:tag_group)
|
||||||
unused_tag_group.tags = [tag4]
|
unused_tag_group.tags = [tag4]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category2),
|
||||||
|
[tag2, tag3],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
|
[tag1, tag2, tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when another category has restricted tags' do
|
context "when another category has restricted tags" do
|
||||||
fab!(:category2) { Fabricate(:category) }
|
fab!(:category2) { Fabricate(:category) }
|
||||||
|
|
||||||
it "doesn't filter tags that are also restricted in another category" do
|
it "doesn't filter tags that are also restricted in another category" do
|
||||||
category2.tags = [tag2, tag3]
|
category2.tags = [tag2, tag3]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category2), [tag2, tag3])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category2),
|
||||||
|
[tag2, tag3],
|
||||||
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag4])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2, tag4])
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
|
[tag1, tag2, tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -278,80 +489,139 @@ RSpec.describe "category tag restrictions" do
|
||||||
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
|
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
|
||||||
tag_group.tags = [tag3, tag4]
|
tag_group.tags = [tag3, tag4]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name, tag3.name]), [tag2, tag4])
|
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
|
||||||
|
[tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, selected_tags: [tag1.name, tag3.name]),
|
||||||
|
[tag2, tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "for tagging a topic, filter_allowed_tags allows tags without parent tag" do
|
it "for tagging a topic, filter_allowed_tags allows tags without parent tag" do
|
||||||
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
|
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
|
||||||
tag_group.tags = [tag3, tag4]
|
tag_group.tags = [tag3, tag4]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_topic: true), [tag1, tag2, tag3, tag4])
|
expect_same_tag_names(filter_allowed_tags(for_topic: true), [tag1, tag2, tag3, tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_topic: true, selected_tags: [tag1.name]), [tag1, tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_topic: true, selected_tags: [tag1.name, tag3.name]), [tag1, tag2, tag3, tag4])
|
filter_allowed_tags(for_topic: true, selected_tags: [tag1.name]),
|
||||||
|
[tag1, tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_topic: true, selected_tags: [tag1.name, tag3.name]),
|
||||||
|
[tag1, tag2, tag3, tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "filter_allowed_tags returns tags common to more than one tag group with parent tag" do
|
it "filter_allowed_tags returns tags common to more than one tag group with parent tag" do
|
||||||
common = Fabricate(:tag, name: 'common')
|
common = Fabricate(:tag, name: "common")
|
||||||
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
|
tag_group = Fabricate(:tag_group, parent_tag_id: tag1.id)
|
||||||
tag_group.tags = [tag2, common]
|
tag_group.tags = [tag2, common]
|
||||||
tag_group = Fabricate(:tag_group, parent_tag_id: tag3.id)
|
tag_group = Fabricate(:tag_group, parent_tag_id: tag3.id)
|
||||||
|
|
||||||
tag_group.tags = [tag4]
|
tag_group.tags = [tag4]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1])
|
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
|
||||||
|
[tag2, tag3, common],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
|
||||||
|
[tag4, tag1],
|
||||||
|
)
|
||||||
|
|
||||||
tag_group.tags = [tag4, common]
|
tag_group.tags = [tag4, common]
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1, common])
|
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
|
||||||
|
[tag2, tag3, common],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
|
||||||
|
[tag4, tag1, common],
|
||||||
|
)
|
||||||
|
|
||||||
parent_tag_group = Fabricate(:tag_group, tags: [tag1, tag3])
|
parent_tag_group = Fabricate(:tag_group, tags: [tag1, tag3])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, tag3, common])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, tag1, common])
|
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
|
||||||
|
[tag2, tag3, common],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
|
||||||
|
[tag4, tag1, common],
|
||||||
|
)
|
||||||
|
|
||||||
parent_tag_group.update!(one_per_topic: true)
|
parent_tag_group.update!(one_per_topic: true)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag3])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag1.name]), [tag2, common])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, selected_tags: [tag3.name]), [tag4, common])
|
filter_allowed_tags(for_input: true, selected_tags: [tag1.name]),
|
||||||
|
[tag2, common],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, selected_tags: [tag3.name]),
|
||||||
|
[tag4, common],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with required tags from tag group' do
|
context "with required tags from tag group" do
|
||||||
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag2]) }
|
fab!(:tag_group) { Fabricate(:tag_group, tags: [tag1, tag2]) }
|
||||||
fab!(:category) { Fabricate(:category, category_required_tag_groups: [CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1)]) }
|
fab!(:category) do
|
||||||
|
Fabricate(
|
||||||
|
:category,
|
||||||
|
category_required_tag_groups: [
|
||||||
|
CategoryRequiredTagGroup.new(tag_group: tag_group, min_count: 1),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
it "search only returns the allowed tags" do
|
it "search only returns the allowed tags" do
|
||||||
tag_group_with_parent = Fabricate(:tag_group, parent_tag_id: tag1.id, tags: [tag3, tag4])
|
tag_group_with_parent = Fabricate(:tag_group, parent_tag_id: tag1.id, tags: [tag3, tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category), [tag1, tag2])
|
expect_same_tag_names(
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]), [tag1])
|
filter_allowed_tags(for_input: true, category: category),
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]), [tag2, tag3, tag4])
|
[tag1, tag2],
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name, tag2.name]), [tag3, tag4])
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag2.name]),
|
||||||
|
[tag1],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: category, selected_tags: [tag1.name]),
|
||||||
|
[tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: category,
|
||||||
|
selected_tags: [tag1.name, tag2.name],
|
||||||
|
),
|
||||||
|
[tag3, tag4],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with category restrictions" do
|
context "with category restrictions" do
|
||||||
fab!(:car_category) { Fabricate(:category) }
|
fab!(:car_category) { Fabricate(:category) }
|
||||||
fab!(:other_category) { Fabricate(:category) }
|
fab!(:other_category) { Fabricate(:category) }
|
||||||
fab!(:makes) { Fabricate(:tag_group, name: "Makes") }
|
fab!(:makes) { Fabricate(:tag_group, name: "Makes") }
|
||||||
fab!(:honda_group) { Fabricate(:tag_group, name: "Honda Models") }
|
fab!(:honda_group) { Fabricate(:tag_group, name: "Honda Models") }
|
||||||
fab!(:ford_group) { Fabricate(:tag_group, name: "Ford Models") }
|
fab!(:ford_group) { Fabricate(:tag_group, name: "Ford Models") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@tags = {}
|
@tags = {}
|
||||||
['honda', 'ford', 'civic', 'accord', 'mustang', 'taurus'].each do |name|
|
%w[honda ford civic accord mustang taurus].each do |name|
|
||||||
@tags[name] = Fabricate(:tag, name: name)
|
@tags[name] = Fabricate(:tag, name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
makes.tags = [@tags['honda'], @tags['ford']]
|
makes.tags = [@tags["honda"], @tags["ford"]]
|
||||||
|
|
||||||
honda_group.parent_tag_id = @tags['honda'].id
|
honda_group.parent_tag_id = @tags["honda"].id
|
||||||
honda_group.save
|
honda_group.save
|
||||||
honda_group.tags = [@tags['civic'], @tags['accord']]
|
honda_group.tags = [@tags["civic"], @tags["accord"]]
|
||||||
|
|
||||||
ford_group.parent_tag_id = @tags['ford'].id
|
ford_group.parent_tag_id = @tags["ford"].id
|
||||||
ford_group.save
|
ford_group.save
|
||||||
ford_group.tags = [@tags['mustang'], @tags['taurus']]
|
ford_group.tags = [@tags["mustang"], @tags["taurus"]]
|
||||||
|
|
||||||
car_category.allowed_tag_groups = [makes.name, honda_group.name, ford_group.name]
|
car_category.allowed_tag_groups = [makes.name, honda_group.name, ford_group.name]
|
||||||
end
|
end
|
||||||
|
@ -359,39 +629,75 @@ RSpec.describe "category tag restrictions" do
|
||||||
it "handles all those rules" do
|
it "handles all those rules" do
|
||||||
# car tags can't be used outside of car category:
|
# car tags can't be used outside of car category:
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2, tag3, tag4])
|
expect_same_tag_names(filter_allowed_tags(for_input: true), [tag1, tag2, tag3, tag4])
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: other_category), [tag1, tag2, tag3, tag4])
|
expect_same_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: other_category),
|
||||||
|
[tag1, tag2, tag3, tag4],
|
||||||
|
)
|
||||||
|
|
||||||
# in car category, a make tag must be given first:
|
# in car category, a make tag must be given first:
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category))).to eq(['ford', 'honda'])
|
expect(
|
||||||
|
sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category)),
|
||||||
|
).to eq(%w[ford honda])
|
||||||
|
|
||||||
# model tags depend on which make is chosen:
|
# model tags depend on which make is chosen:
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic', 'ford'])
|
expect(
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['honda', 'mustang', 'taurus'])
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]),
|
||||||
|
),
|
||||||
|
).to eq(%w[accord civic ford])
|
||||||
|
expect(
|
||||||
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
|
||||||
|
),
|
||||||
|
).to eq(%w[honda mustang taurus])
|
||||||
|
|
||||||
makes.update!(one_per_topic: true)
|
makes.update!(one_per_topic: true)
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic'])
|
expect(
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus'])
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]),
|
||||||
|
),
|
||||||
|
).to eq(%w[accord civic])
|
||||||
|
expect(
|
||||||
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
|
||||||
|
),
|
||||||
|
).to eq(%w[mustang taurus])
|
||||||
|
|
||||||
honda_group.update!(one_per_topic: true)
|
honda_group.update!(one_per_topic: true)
|
||||||
ford_group.update!(one_per_topic: true)
|
ford_group.update!(one_per_topic: true)
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic'])
|
expect(
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus'])
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["honda"]),
|
||||||
|
),
|
||||||
|
).to eq(%w[accord civic])
|
||||||
|
expect(
|
||||||
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
|
||||||
|
),
|
||||||
|
).to eq(%w[mustang taurus])
|
||||||
|
|
||||||
car_category.update!(allow_global_tags: true)
|
car_category.update!(allow_global_tags: true)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category),
|
expect_same_tag_names(
|
||||||
['ford', 'honda', tag1, tag2, tag3, tag4]
|
filter_allowed_tags(for_input: true, category: car_category),
|
||||||
|
["ford", "honda", tag1, tag2, tag3, tag4],
|
||||||
)
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']),
|
expect_same_tag_names(
|
||||||
['mustang', 'taurus', tag1, tag2, tag3, tag4]
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
|
||||||
|
["mustang", "taurus", tag1, tag2, tag3, tag4],
|
||||||
)
|
)
|
||||||
expect_same_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford', 'mustang']),
|
expect_same_tag_names(
|
||||||
[tag1, tag2, tag3, tag4]
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: car_category,
|
||||||
|
selected_tags: %w[ford mustang],
|
||||||
|
),
|
||||||
|
[tag1, tag2, tag3, tag4],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can apply the tags to a topic" do
|
it "can apply the tags to a topic" do
|
||||||
post = create_post(category: car_category, tags: ['ford', 'mustang'])
|
post = create_post(category: car_category, tags: %w[ford mustang])
|
||||||
expect(post.topic.tags.map(&:name).sort).to eq(['ford', 'mustang'])
|
expect(post.topic.tags.map(&:name).sort).to eq(%w[ford mustang])
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with limit one tag from each group" do
|
context "with limit one tag from each group" do
|
||||||
|
@ -402,24 +708,51 @@ RSpec.describe "category tag restrictions" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can restrict one tag from each group" do
|
it "can restrict one tag from each group" do
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category))).to eq(['ford', 'honda'])
|
expect(
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda']))).to eq(['accord', 'civic'])
|
sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category)),
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford']))).to eq(['mustang', 'taurus'])
|
).to eq(%w[ford honda])
|
||||||
expect(sorted_tag_names(filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['ford', 'mustang']))).to eq([])
|
expect(
|
||||||
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: car_category,
|
||||||
|
selected_tags: ["honda"],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).to eq(%w[accord civic])
|
||||||
|
expect(
|
||||||
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(for_input: true, category: car_category, selected_tags: ["ford"]),
|
||||||
|
),
|
||||||
|
).to eq(%w[mustang taurus])
|
||||||
|
expect(
|
||||||
|
sorted_tag_names(
|
||||||
|
filter_allowed_tags(
|
||||||
|
for_input: true,
|
||||||
|
category: car_category,
|
||||||
|
selected_tags: %w[ford mustang],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).to eq([])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can apply the tags to a topic" do
|
it "can apply the tags to a topic" do
|
||||||
post = create_post(category: car_category, tags: ['ford', 'mustang'])
|
post = create_post(category: car_category, tags: %w[ford mustang])
|
||||||
expect(post.topic.tags.map(&:name).sort).to eq(['ford', 'mustang'])
|
expect(post.topic.tags.map(&:name).sort).to eq(%w[ford mustang])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "can remove extra tags from the same group" do
|
it "can remove extra tags from the same group" do
|
||||||
# A weird case that input field wouldn't allow.
|
# A weird case that input field wouldn't allow.
|
||||||
# Only one tag from car makers is allowed, but we're saying that two have been selected.
|
# Only one tag from car makers is allowed, but we're saying that two have been selected.
|
||||||
names = filter_allowed_tags(for_input: true, category: car_category, selected_tags: ['honda', 'ford']).map(&:name)
|
names =
|
||||||
expect(names.include?('honda') || names.include?('ford')).to eq(false)
|
filter_allowed_tags(
|
||||||
expect(names).to include('civic')
|
for_input: true,
|
||||||
expect(names).to include('mustang')
|
category: car_category,
|
||||||
|
selected_tags: %w[honda ford],
|
||||||
|
).map(&:name)
|
||||||
|
expect(names.include?("honda") || names.include?("ford")).to eq(false)
|
||||||
|
expect(names).to include("civic")
|
||||||
|
expect(names).to include("mustang")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -441,9 +774,9 @@ RSpec.describe "tag topic counts per category" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "counts when a topic is created with tags" do
|
it "counts when a topic is created with tags" do
|
||||||
expect {
|
expect { Fabricate(:topic, category: category, tags: [tag1, tag2]) }.to change {
|
||||||
Fabricate(:topic, category: category, tags: [tag1, tag2])
|
CategoryTagStat.count
|
||||||
}.to change { CategoryTagStat.count }.by(2)
|
}.by(2)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(1)
|
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(1)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(1)
|
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(1)
|
||||||
end
|
end
|
||||||
|
@ -461,7 +794,7 @@ RSpec.describe "tag topic counts per category" do
|
||||||
|
|
||||||
context "with topic with 2 tags" do
|
context "with topic with 2 tags" do
|
||||||
fab!(:topic) { Fabricate(:topic, category: category, tags: [tag1, tag2]) }
|
fab!(:topic) { Fabricate(:topic, category: category, tags: [tag1, tag2]) }
|
||||||
fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
|
fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
|
||||||
|
|
||||||
it "has correct counts after tag is removed from a topic" do
|
it "has correct counts after tag is removed from a topic" do
|
||||||
post
|
post
|
||||||
|
@ -473,7 +806,12 @@ RSpec.describe "tag topic counts per category" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "has correct counts after a topic's category changes" do
|
it "has correct counts after a topic's category changes" do
|
||||||
PostRevisor.new(post).revise!(topic.user, category_id: category2.id, raw: post.raw, tags: [tag1.name, tag2.name])
|
PostRevisor.new(post).revise!(
|
||||||
|
topic.user,
|
||||||
|
category_id: category2.id,
|
||||||
|
raw: post.raw,
|
||||||
|
tags: [tag1.name, tag2.name],
|
||||||
|
)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
|
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0)
|
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0)
|
||||||
expect(CategoryTagStat.where(category: category2, tag: tag1).sum(:topic_count)).to eq(1)
|
expect(CategoryTagStat.where(category: category2, tag: tag1).sum(:topic_count)).to eq(1)
|
||||||
|
@ -481,7 +819,12 @@ RSpec.describe "tag topic counts per category" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "has correct counts after topic's category AND tags changed" do
|
it "has correct counts after topic's category AND tags changed" do
|
||||||
PostRevisor.new(post).revise!(topic.user, raw: post.raw, tags: [tag2.name, tag3.name], category_id: category2.id)
|
PostRevisor.new(post).revise!(
|
||||||
|
topic.user,
|
||||||
|
raw: post.raw,
|
||||||
|
tags: [tag2.name, tag3.name],
|
||||||
|
category_id: category2.id,
|
||||||
|
)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
|
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0)
|
expect(CategoryTagStat.where(category: category, tag: tag2).sum(:topic_count)).to eq(0)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag3).sum(:topic_count)).to eq(0)
|
expect(CategoryTagStat.where(category: category, tag: tag3).sum(:topic_count)).to eq(0)
|
||||||
|
@ -496,8 +839,18 @@ RSpec.describe "tag topic counts per category" do
|
||||||
fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
|
fab!(:post) { Fabricate(:post, user: topic.user, topic: topic) }
|
||||||
|
|
||||||
it "counts after topic becomes uncategorized" do
|
it "counts after topic becomes uncategorized" do
|
||||||
PostRevisor.new(post).revise!(topic.user, raw: post.raw, tags: [tag1.name], category_id: SiteSetting.uncategorized_category_id)
|
PostRevisor.new(post).revise!(
|
||||||
expect(CategoryTagStat.where(category: Category.find(SiteSetting.uncategorized_category_id), tag: tag1).sum(:topic_count)).to eq(1)
|
topic.user,
|
||||||
|
raw: post.raw,
|
||||||
|
tags: [tag1.name],
|
||||||
|
category_id: SiteSetting.uncategorized_category_id,
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
CategoryTagStat.where(
|
||||||
|
category: Category.find(SiteSetting.uncategorized_category_id),
|
||||||
|
tag: tag1,
|
||||||
|
).sum(:topic_count),
|
||||||
|
).to eq(1)
|
||||||
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
|
expect(CategoryTagStat.where(category: category, tag: tag1).sum(:topic_count)).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'content security policy integration' do
|
RSpec.describe "content security policy integration" do
|
||||||
|
|
||||||
it "adds the csp headers correctly" do
|
it "adds the csp headers correctly" do
|
||||||
SiteSetting.content_security_policy = false
|
SiteSetting.content_security_policy = false
|
||||||
get "/"
|
get "/"
|
||||||
|
@ -15,8 +14,10 @@ RSpec.describe 'content security policy integration' do
|
||||||
context "with different hostnames" do
|
context "with different hostnames" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.content_security_policy = true
|
SiteSetting.content_security_policy = true
|
||||||
RailsMultisite::ConnectionManagement.stubs(:current_db_hostnames).returns(['primary.example.com', 'secondary.example.com'])
|
RailsMultisite::ConnectionManagement.stubs(:current_db_hostnames).returns(
|
||||||
RailsMultisite::ConnectionManagement.stubs(:current_hostname).returns('primary.example.com')
|
%w[primary.example.com secondary.example.com],
|
||||||
|
)
|
||||||
|
RailsMultisite::ConnectionManagement.stubs(:current_hostname).returns("primary.example.com")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "works with the primary domain" do
|
it "works with the primary domain" do
|
||||||
|
@ -52,5 +53,4 @@ RSpec.describe 'content security policy integration' do
|
||||||
expect(response.headers["Content-Security-Policy"]).to include("https://test.localhost")
|
expect(response.headers["Content-Security-Policy"]).to include("https://test.localhost")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
describe 'Discord OAuth2' do
|
describe "Discord OAuth2" do
|
||||||
let(:access_token) { "discord_access_token_448" }
|
let(:access_token) { "discord_access_token_448" }
|
||||||
let(:client_id) { "abcdef11223344" }
|
let(:client_id) { "abcdef11223344" }
|
||||||
let(:client_secret) { "adddcccdddd99922" }
|
let(:client_secret) { "adddcccdddd99922" }
|
||||||
|
@ -9,15 +9,14 @@ describe 'Discord OAuth2' do
|
||||||
fab!(:user1) { Fabricate(:user) }
|
fab!(:user1) { Fabricate(:user) }
|
||||||
|
|
||||||
def setup_discord_email_stub(email, verified:)
|
def setup_discord_email_stub(email, verified:)
|
||||||
stub_request(:get, "https://discord.com/api/users/@me")
|
stub_request(:get, "https://discord.com/api/users/@me").with(
|
||||||
.with(
|
headers: {
|
||||||
headers: {
|
"Authorization" => "Bearer #{access_token}",
|
||||||
"Authorization" => "Bearer #{access_token}"
|
},
|
||||||
}
|
).to_return(
|
||||||
)
|
status: 200,
|
||||||
.to_return(
|
body:
|
||||||
status: 200,
|
JSON.dump(
|
||||||
body: JSON.dump(
|
|
||||||
id: "80351110224678912",
|
id: "80351110224678912",
|
||||||
username: "Nelly",
|
username: "Nelly",
|
||||||
discriminator: "1337",
|
discriminator: "1337",
|
||||||
|
@ -26,14 +25,14 @@ describe 'Discord OAuth2' do
|
||||||
email: email,
|
email: email,
|
||||||
flags: 64,
|
flags: 64,
|
||||||
banner: "06c16474723fe537c283b8efa61a30c8",
|
banner: "06c16474723fe537c283b8efa61a30c8",
|
||||||
accent_color: 16711680,
|
accent_color: 16_711_680,
|
||||||
premium_type: 1,
|
premium_type: 1,
|
||||||
public_flags: 64
|
public_flags: 64,
|
||||||
),
|
),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type" => "application/json"
|
"Content-Type" => "application/json",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -41,50 +40,49 @@ describe 'Discord OAuth2' do
|
||||||
SiteSetting.discord_client_id = client_id
|
SiteSetting.discord_client_id = client_id
|
||||||
SiteSetting.discord_secret = client_secret
|
SiteSetting.discord_secret = client_secret
|
||||||
|
|
||||||
stub_request(:post, "https://discord.com/api/oauth2/token")
|
stub_request(:post, "https://discord.com/api/oauth2/token").with(
|
||||||
.with(
|
body:
|
||||||
body: hash_including(
|
hash_including(
|
||||||
"client_id" => client_id,
|
"client_id" => client_id,
|
||||||
"client_secret" => client_secret,
|
"client_secret" => client_secret,
|
||||||
"code" => temp_code,
|
"code" => temp_code,
|
||||||
"grant_type" => "authorization_code",
|
"grant_type" => "authorization_code",
|
||||||
"redirect_uri" => "http://test.localhost/auth/discord/callback"
|
"redirect_uri" => "http://test.localhost/auth/discord/callback",
|
||||||
)
|
),
|
||||||
)
|
).to_return(
|
||||||
.to_return(
|
status: 200,
|
||||||
status: 200,
|
body:
|
||||||
body: Rack::Utils.build_query(
|
Rack::Utils.build_query(
|
||||||
access_token: access_token,
|
access_token: access_token,
|
||||||
scope: "identify emails guilds",
|
scope: "identify emails guilds",
|
||||||
token_type: "Bearer",
|
token_type: "Bearer",
|
||||||
expires_in: 604800,
|
expires_in: 604_800,
|
||||||
refresh_token: "D43f5y0ahjqew82jZ4NViEr2YafMKhue",
|
refresh_token: "D43f5y0ahjqew82jZ4NViEr2YafMKhue",
|
||||||
),
|
),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type" => "application/x-www-form-urlencoded"
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
stub_request(:get, "https://discord.com/api/users/@me/guilds")
|
stub_request(:get, "https://discord.com/api/users/@me/guilds").with(
|
||||||
.with(
|
headers: {
|
||||||
headers: {
|
"Authorization" => "Bearer #{access_token}",
|
||||||
"Authorization" => "Bearer #{access_token}"
|
},
|
||||||
}
|
).to_return(
|
||||||
)
|
status: 200,
|
||||||
.to_return(
|
body:
|
||||||
status: 200,
|
JSON.dump(
|
||||||
body: JSON.dump(
|
|
||||||
id: "80351110224678912",
|
id: "80351110224678912",
|
||||||
name: "1337 Krew",
|
name: "1337 Krew",
|
||||||
icon: "8342729096ea3675442027381ff50dfe",
|
icon: "8342729096ea3675442027381ff50dfe",
|
||||||
owner: true,
|
owner: true,
|
||||||
permissions: "36953089",
|
permissions: "36953089",
|
||||||
features: ["COMMUNITY", "NEWS"]
|
features: %w[COMMUNITY NEWS],
|
||||||
),
|
),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type" => "application/json"
|
"Content-Type" => "application/json",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't sign in anyone if the email from discord is not verified" do
|
it "doesn't sign in anyone if the email from discord is not verified" do
|
||||||
|
@ -94,10 +92,7 @@ describe 'Discord OAuth2' do
|
||||||
|
|
||||||
setup_discord_email_stub(user1.email, verified: false)
|
setup_discord_email_stub(user1.email, verified: false)
|
||||||
|
|
||||||
post "/auth/discord/callback", params: {
|
post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
|
@ -111,10 +106,7 @@ describe 'Discord OAuth2' do
|
||||||
|
|
||||||
setup_discord_email_stub(user1.email, verified: true)
|
setup_discord_email_stub(user1.email, verified: true)
|
||||||
|
|
||||||
post "/auth/discord/callback", params: {
|
post "/auth/discord/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
|
|
|
@ -6,14 +6,14 @@ RSpec.describe EmailStyle do
|
||||||
SiteSetting.email_custom_template = "<hello>%{email_content}</hello><%= (111 * 333) %>"
|
SiteSetting.email_custom_template = "<hello>%{email_content}</hello><%= (111 * 333) %>"
|
||||||
html = Email::Renderer.new(UserNotifications.signup(Fabricate(:user))).html
|
html = Email::Renderer.new(UserNotifications.signup(Fabricate(:user))).html
|
||||||
expect(html).not_to include("36963")
|
expect(html).not_to include("36963")
|
||||||
expect(html).to include('<hello>')
|
expect(html).to include("<hello>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with a custom template" do
|
context "with a custom template" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.email_custom_template = "<body><h1>FOR YOU</h1><div>%{email_content}</div></body>"
|
SiteSetting.email_custom_template = "<body><h1>FOR YOU</h1><div>%{email_content}</div></body>"
|
||||||
SiteSetting.email_custom_css = 'h1 { color: red; } div.body { color: #FAB; }'
|
SiteSetting.email_custom_css = "h1 { color: red; } div.body { color: #FAB; }"
|
||||||
SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
|
SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,36 +22,36 @@ RSpec.describe EmailStyle do
|
||||||
SiteSetting.remove_override!(:email_custom_css)
|
SiteSetting.remove_override!(:email_custom_css)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with invite' do
|
context "with invite" do
|
||||||
fab!(:invite) { Fabricate(:invite) }
|
fab!(:invite) { Fabricate(:invite) }
|
||||||
let(:invite_mail) { InviteMailer.send_invite(invite) }
|
let(:invite_mail) { InviteMailer.send_invite(invite) }
|
||||||
|
|
||||||
subject(:mail_html) { Email::Renderer.new(invite_mail).html }
|
subject(:mail_html) { Email::Renderer.new(invite_mail).html }
|
||||||
|
|
||||||
it 'applies customizations' do
|
it "applies customizations" do
|
||||||
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
||||||
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
|
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'applies customizations if compiled is missing' do
|
it "applies customizations if compiled is missing" do
|
||||||
SiteSetting.remove_override!(:email_custom_css_compiled)
|
SiteSetting.remove_override!(:email_custom_css_compiled)
|
||||||
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
||||||
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
|
expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can apply RTL attrs' do
|
it "can apply RTL attrs" do
|
||||||
SiteSetting.default_locale = 'he'
|
SiteSetting.default_locale = "he"
|
||||||
body_attrs = mail_html.match(/<body ([^>])+/)
|
body_attrs = mail_html.match(/<body ([^>])+/)
|
||||||
expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/)
|
expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/)
|
||||||
expect(body_attrs[0]&.downcase).to include('dir="rtl"')
|
expect(body_attrs[0]&.downcase).to include('dir="rtl"')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user_replied' do
|
context "when user_replied" do
|
||||||
let(:response_by_user) { Fabricate(:user, name: "John Doe") }
|
let(:response_by_user) { Fabricate(:user, name: "John Doe") }
|
||||||
let(:category) { Fabricate(:category, name: 'India') }
|
let(:category) { Fabricate(:category, name: "India") }
|
||||||
let(:topic) { Fabricate(:topic, category: category, title: "Super cool topic") }
|
let(:topic) { Fabricate(:topic, category: category, title: "Super cool topic") }
|
||||||
let(:post) { Fabricate(:post, topic: topic, raw: 'This is My super duper cool topic') }
|
let(:post) { Fabricate(:post, topic: topic, raw: "This is My super duper cool topic") }
|
||||||
let(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) }
|
let(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) }
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:notification) { Fabricate(:replied_notification, user: user, post: response) }
|
let(:notification) { Fabricate(:replied_notification, user: user, post: response) }
|
||||||
|
@ -61,7 +61,7 @@ RSpec.describe EmailStyle do
|
||||||
user,
|
user,
|
||||||
post: response,
|
post: response,
|
||||||
notification_type: notification.notification_type,
|
notification_type: notification.notification_type,
|
||||||
notification_data_hash: notification.data_hash
|
notification_data_hash: notification.data_hash,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,42 +72,45 @@ RSpec.describe EmailStyle do
|
||||||
|
|
||||||
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
||||||
matches = mail_html.match(/<div style="([^"]+)" dm=\"body\">#{post.raw}/)
|
matches = mail_html.match(/<div style="([^"]+)" dm=\"body\">#{post.raw}/)
|
||||||
expect(matches[1]).to include('color: #FAB;') # custom
|
expect(matches[1]).to include("color: #FAB;") # custom
|
||||||
expect(matches[1]).to include('padding-top:5px;') # div.body
|
expect(matches[1]).to include("padding-top:5px;") # div.body
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: translation override
|
# TODO: translation override
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with signup' do
|
context "with signup" do
|
||||||
let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) }
|
let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) }
|
||||||
subject(:mail_html) { Email::Renderer.new(signup_mail).html }
|
subject(:mail_html) { Email::Renderer.new(signup_mail).html }
|
||||||
|
|
||||||
it "customizations are applied to html part of emails" do
|
it "customizations are applied to html part of emails" do
|
||||||
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
||||||
expect(mail_html).to include('activate-account')
|
expect(mail_html).to include("activate-account")
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with translation override' do
|
context "with translation override" do
|
||||||
before do
|
before do
|
||||||
TranslationOverride.upsert!(
|
TranslationOverride.upsert!(
|
||||||
SiteSetting.default_locale,
|
SiteSetting.default_locale,
|
||||||
'user_notifications.signup.text_body_template',
|
"user_notifications.signup.text_body_template",
|
||||||
"CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}"
|
"CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
after do
|
after do
|
||||||
TranslationOverride.revert!(SiteSetting.default_locale, ['user_notifications.signup.text_body_template'])
|
TranslationOverride.revert!(
|
||||||
|
SiteSetting.default_locale,
|
||||||
|
["user_notifications.signup.text_body_template"],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "applies customizations when translation override exists" do
|
it "applies customizations when translation override exists" do
|
||||||
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
expect(mail_html.scan('<h1 style="color: red;">FOR YOU</h1>').count).to eq(1)
|
||||||
expect(mail_html.scan('CLICK THAT LINK').count).to eq(1)
|
expect(mail_html.scan("CLICK THAT LINK").count).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with some bad css' do
|
context "with some bad css" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; '
|
SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; '
|
||||||
SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
|
SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css
|
||||||
|
@ -115,13 +118,15 @@ RSpec.describe EmailStyle do
|
||||||
|
|
||||||
it "can render the html" do
|
it "can render the html" do
|
||||||
expect(mail_html.scan(/<h1\s*(?:style=""){0,1}>FOR YOU<\/h1>/).count).to eq(1)
|
expect(mail_html.scan(/<h1\s*(?:style=""){0,1}>FOR YOU<\/h1>/).count).to eq(1)
|
||||||
expect(mail_html).to include('activate-account')
|
expect(mail_html).to include("activate-account")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with digest' do
|
context "with digest" do
|
||||||
fab!(:popular_topic) { Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) }
|
fab!(:popular_topic) do
|
||||||
|
Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago)
|
||||||
|
end
|
||||||
let(:summary_email) { UserNotifications.digest(Fabricate(:user)) }
|
let(:summary_email) { UserNotifications.digest(Fabricate(:user)) }
|
||||||
subject(:mail_html) { Email::Renderer.new(summary_email).html }
|
subject(:mail_html) { Email::Renderer.new(summary_email).html }
|
||||||
|
|
||||||
|
@ -133,7 +138,7 @@ RSpec.describe EmailStyle do
|
||||||
it "doesn't apply customizations if apply_custom_styles_to_digest is disabled" do
|
it "doesn't apply customizations if apply_custom_styles_to_digest is disabled" do
|
||||||
SiteSetting.apply_custom_styles_to_digest = false
|
SiteSetting.apply_custom_styles_to_digest = false
|
||||||
expect(mail_html).to_not include('<h1 style="color: red;">FOR YOU</h1>')
|
expect(mail_html).to_not include('<h1 style="color: red;">FOR YOU</h1>')
|
||||||
expect(mail_html).to_not include('FOR YOU')
|
expect(mail_html).to_not include("FOR YOU")
|
||||||
expect(mail_html).to include(popular_topic.title)
|
expect(mail_html).to include(popular_topic.title)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
describe 'Facebook OAuth2' do
|
describe "Facebook OAuth2" do
|
||||||
let(:access_token) { "facebook_access_token_448" }
|
let(:access_token) { "facebook_access_token_448" }
|
||||||
let(:app_id) { "432489234823984" }
|
let(:app_id) { "432489234823984" }
|
||||||
let(:app_secret) { "adddcccdddd99922" }
|
let(:app_secret) { "adddcccdddd99922" }
|
||||||
let(:temp_code) { "facebook_temp_code_544254" }
|
let(:temp_code) { "facebook_temp_code_544254" }
|
||||||
let(:appsecret_proof) { OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, app_secret, access_token) }
|
let(:appsecret_proof) do
|
||||||
|
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, app_secret, access_token)
|
||||||
|
end
|
||||||
|
|
||||||
fab!(:user1) { Fabricate(:user) }
|
fab!(:user1) { Fabricate(:user) }
|
||||||
|
|
||||||
|
@ -18,19 +20,16 @@ describe 'Facebook OAuth2' do
|
||||||
}
|
}
|
||||||
body[:email] = email if email
|
body[:email] = email if email
|
||||||
|
|
||||||
stub_request(:get, "https://graph.facebook.com/v5.0/me?appsecret_proof=#{appsecret_proof}&fields=name,first_name,last_name,email")
|
stub_request(
|
||||||
.with(
|
:get,
|
||||||
headers: {
|
"https://graph.facebook.com/v5.0/me?appsecret_proof=#{appsecret_proof}&fields=name,first_name,last_name,email",
|
||||||
"Authorization" => "OAuth #{access_token}"
|
).with(headers: { "Authorization" => "OAuth #{access_token}" }).to_return(
|
||||||
}
|
status: 200,
|
||||||
)
|
body: JSON.dump(body),
|
||||||
.to_return(
|
headers: {
|
||||||
status: 200,
|
"Content-Type" => "application/json",
|
||||||
body: JSON.dump(body),
|
},
|
||||||
headers: {
|
)
|
||||||
"Content-Type" => "application/json"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -38,27 +37,23 @@ describe 'Facebook OAuth2' do
|
||||||
SiteSetting.facebook_app_id = app_id
|
SiteSetting.facebook_app_id = app_id
|
||||||
SiteSetting.facebook_app_secret = app_secret
|
SiteSetting.facebook_app_secret = app_secret
|
||||||
|
|
||||||
stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token")
|
stub_request(:post, "https://graph.facebook.com/v5.0/oauth/access_token").with(
|
||||||
.with(
|
body:
|
||||||
body: hash_including(
|
hash_including(
|
||||||
"client_id" => app_id,
|
"client_id" => app_id,
|
||||||
"client_secret" => app_secret,
|
"client_secret" => app_secret,
|
||||||
"code" => temp_code,
|
"code" => temp_code,
|
||||||
"grant_type" => "authorization_code",
|
"grant_type" => "authorization_code",
|
||||||
"redirect_uri" => "http://test.localhost/auth/facebook/callback"
|
"redirect_uri" => "http://test.localhost/auth/facebook/callback",
|
||||||
)
|
|
||||||
)
|
|
||||||
.to_return(
|
|
||||||
status: 200,
|
|
||||||
body: Rack::Utils.build_query(
|
|
||||||
access_token: access_token,
|
|
||||||
scope: "email",
|
|
||||||
token_type: "Bearer",
|
|
||||||
),
|
),
|
||||||
headers: {
|
).to_return(
|
||||||
"Content-Type" => "application/x-www-form-urlencoded"
|
status: 200,
|
||||||
}
|
body:
|
||||||
)
|
Rack::Utils.build_query(access_token: access_token, scope: "email", token_type: "Bearer"),
|
||||||
|
headers: {
|
||||||
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "signs in the user if the API response from facebook includes an email (implies it's verified) and the email matches an existing user's" do
|
it "signs in the user if the API response from facebook includes an email (implies it's verified) and the email matches an existing user's" do
|
||||||
|
@ -68,10 +63,7 @@ describe 'Facebook OAuth2' do
|
||||||
|
|
||||||
setup_facebook_email_stub(email: user1.email)
|
setup_facebook_email_stub(email: user1.email)
|
||||||
|
|
||||||
post "/auth/facebook/callback", params: {
|
post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
|
@ -85,10 +77,7 @@ describe 'Facebook OAuth2' do
|
||||||
|
|
||||||
setup_facebook_email_stub(email: nil)
|
setup_facebook_email_stub(email: nil)
|
||||||
|
|
||||||
post "/auth/facebook/callback", params: {
|
post "/auth/facebook/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe PostAction do
|
RSpec.describe PostAction do
|
||||||
|
|
||||||
it "triggers the 'flag_reviewed' event when there was at least one flag" do
|
it "triggers the 'flag_reviewed' event when there was at least one flag" do
|
||||||
admin = Fabricate(:admin)
|
admin = Fabricate(:admin)
|
||||||
|
|
||||||
|
@ -14,5 +13,4 @@ RSpec.describe PostAction do
|
||||||
events = DiscourseEvent.track_events { PostDestroyer.new(admin, flagged_post).destroy }
|
events = DiscourseEvent.track_events { PostDestroyer.new(admin, flagged_post).destroy }
|
||||||
expect(events.map { |e| e[:event_name] }).to include(:flag_reviewed)
|
expect(events.map { |e| e[:event_name] }).to include(:flag_reviewed)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
describe 'GitHub Oauth2' do
|
describe "GitHub Oauth2" do
|
||||||
let(:access_token) { "github_access_token_448" }
|
let(:access_token) { "github_access_token_448" }
|
||||||
let(:client_id) { "abcdef11223344" }
|
let(:client_id) { "abcdef11223344" }
|
||||||
let(:client_secret) { "adddcccdddd99922" }
|
let(:client_secret) { "adddcccdddd99922" }
|
||||||
|
@ -10,19 +10,17 @@ describe 'GitHub Oauth2' do
|
||||||
fab!(:user2) { Fabricate(:user) }
|
fab!(:user2) { Fabricate(:user) }
|
||||||
|
|
||||||
def setup_github_emails_stub(emails)
|
def setup_github_emails_stub(emails)
|
||||||
stub_request(:get, "https://api.github.com/user/emails")
|
stub_request(:get, "https://api.github.com/user/emails").with(
|
||||||
.with(
|
headers: {
|
||||||
headers: {
|
"Authorization" => "Bearer #{access_token}",
|
||||||
"Authorization" => "Bearer #{access_token}"
|
},
|
||||||
}
|
).to_return(
|
||||||
)
|
status: 200,
|
||||||
.to_return(
|
body: JSON.dump(emails),
|
||||||
status: 200,
|
headers: {
|
||||||
body: JSON.dump(emails),
|
"Content-Type" => "application/json",
|
||||||
headers: {
|
},
|
||||||
"Content-Type" => "application/json"
|
)
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -30,35 +28,34 @@ describe 'GitHub Oauth2' do
|
||||||
SiteSetting.github_client_id = client_id
|
SiteSetting.github_client_id = client_id
|
||||||
SiteSetting.github_client_secret = client_secret
|
SiteSetting.github_client_secret = client_secret
|
||||||
|
|
||||||
stub_request(:post, "https://github.com/login/oauth/access_token")
|
stub_request(:post, "https://github.com/login/oauth/access_token").with(
|
||||||
.with(
|
body:
|
||||||
body: hash_including(
|
hash_including(
|
||||||
"client_id" => client_id,
|
"client_id" => client_id,
|
||||||
"client_secret" => client_secret,
|
"client_secret" => client_secret,
|
||||||
"code" => temp_code,
|
"code" => temp_code,
|
||||||
)
|
),
|
||||||
)
|
).to_return(
|
||||||
.to_return(
|
status: 200,
|
||||||
status: 200,
|
body:
|
||||||
body: Rack::Utils.build_query(
|
Rack::Utils.build_query(
|
||||||
access_token: access_token,
|
access_token: access_token,
|
||||||
scope: "user:email",
|
scope: "user:email",
|
||||||
token_type: "bearer"
|
token_type: "bearer",
|
||||||
),
|
),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type" => "application/x-www-form-urlencoded"
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
stub_request(:get, "https://api.github.com/user")
|
stub_request(:get, "https://api.github.com/user").with(
|
||||||
.with(
|
headers: {
|
||||||
headers: {
|
"Authorization" => "Bearer #{access_token}",
|
||||||
"Authorization" => "Bearer #{access_token}"
|
},
|
||||||
}
|
).to_return(
|
||||||
)
|
status: 200,
|
||||||
.to_return(
|
body:
|
||||||
status: 200,
|
JSON.dump(
|
||||||
body: JSON.dump(
|
|
||||||
login: "octocat",
|
login: "octocat",
|
||||||
id: 1,
|
id: 1,
|
||||||
node_id: "MDQ6VXNlcjE=",
|
node_id: "MDQ6VXNlcjE=",
|
||||||
|
@ -94,20 +91,20 @@ describe 'GitHub Oauth2' do
|
||||||
private_gists: 81,
|
private_gists: 81,
|
||||||
total_private_repos: 100,
|
total_private_repos: 100,
|
||||||
owned_private_repos: 100,
|
owned_private_repos: 100,
|
||||||
disk_usage: 10000,
|
disk_usage: 10_000,
|
||||||
collaborators: 8,
|
collaborators: 8,
|
||||||
two_factor_authentication: true,
|
two_factor_authentication: true,
|
||||||
plan: {
|
plan: {
|
||||||
name: "Medium",
|
name: "Medium",
|
||||||
space: 400,
|
space: 400,
|
||||||
private_repos: 20,
|
private_repos: 20,
|
||||||
collaborators: 0
|
collaborators: 0,
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type" => "application/json"
|
"Content-Type" => "application/json",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't sign in anyone if none of the emails from github are verified" do
|
it "doesn't sign in anyone if none of the emails from github are verified" do
|
||||||
|
@ -117,25 +114,12 @@ describe 'GitHub Oauth2' do
|
||||||
|
|
||||||
setup_github_emails_stub(
|
setup_github_emails_stub(
|
||||||
[
|
[
|
||||||
{
|
{ email: user1.email, primary: true, verified: false, visibility: "private" },
|
||||||
email: user1.email,
|
{ email: user2.email, primary: false, verified: false, visibility: "private" },
|
||||||
primary: true,
|
],
|
||||||
verified: false,
|
|
||||||
visibility: "private"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
email: user2.email,
|
|
||||||
primary: false,
|
|
||||||
verified: false,
|
|
||||||
visibility: "private"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
post "/auth/github/callback", params: {
|
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
expect(session[:current_user_id]).to be_blank
|
expect(session[:current_user_id]).to be_blank
|
||||||
|
@ -148,25 +132,12 @@ describe 'GitHub Oauth2' do
|
||||||
|
|
||||||
setup_github_emails_stub(
|
setup_github_emails_stub(
|
||||||
[
|
[
|
||||||
{
|
{ email: user1.email, primary: true, verified: false, visibility: "private" },
|
||||||
email: user1.email,
|
{ email: user2.email, primary: false, verified: true, visibility: "private" },
|
||||||
primary: true,
|
],
|
||||||
verified: false,
|
|
||||||
visibility: "private"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
email: user2.email,
|
|
||||||
primary: false,
|
|
||||||
verified: true,
|
|
||||||
visibility: "private"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
post "/auth/github/callback", params: {
|
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
expect(session[:current_user_id]).to eq(user2.id)
|
expect(session[:current_user_id]).to eq(user2.id)
|
||||||
|
@ -183,21 +154,13 @@ describe 'GitHub Oauth2' do
|
||||||
email: "somerandomemail@discourse.org",
|
email: "somerandomemail@discourse.org",
|
||||||
primary: true,
|
primary: true,
|
||||||
verified: true,
|
verified: true,
|
||||||
visibility: "private"
|
visibility: "private",
|
||||||
},
|
},
|
||||||
{
|
{ email: user2.email, primary: false, verified: false, visibility: "private" },
|
||||||
email: user2.email,
|
],
|
||||||
primary: false,
|
|
||||||
verified: false,
|
|
||||||
visibility: "private"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
post "/auth/github/callback", params: {
|
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
expect(session[:current_user_id]).to be_blank
|
expect(session[:current_user_id]).to be_blank
|
||||||
|
@ -210,25 +173,12 @@ describe 'GitHub Oauth2' do
|
||||||
|
|
||||||
setup_github_emails_stub(
|
setup_github_emails_stub(
|
||||||
[
|
[
|
||||||
{
|
{ email: user1.email, primary: true, verified: true, visibility: "private" },
|
||||||
email: user1.email,
|
{ email: user2.email, primary: false, verified: true, visibility: "private" },
|
||||||
primary: true,
|
],
|
||||||
verified: true,
|
|
||||||
visibility: "private"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
email: user2.email,
|
|
||||||
primary: false,
|
|
||||||
verified: true,
|
|
||||||
visibility: "private"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
post "/auth/github/callback", params: {
|
post "/auth/github/callback", params: { state: session["omniauth.state"], code: temp_code }
|
||||||
state: session["omniauth.state"],
|
|
||||||
code: temp_code
|
|
||||||
}
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
expect(response.location).to eq("http://test.localhost/")
|
expect(response.location).to eq("http://test.localhost/")
|
||||||
expect(session[:current_user_id]).to eq(user1.id)
|
expect(session[:current_user_id]).to eq(user1.id)
|
||||||
|
|
|
@ -6,18 +6,20 @@ RSpec.describe Group do
|
||||||
:group,
|
:group,
|
||||||
visibility_level: Group.visibility_levels[:public],
|
visibility_level: Group.visibility_levels[:public],
|
||||||
mentionable_level: Group::ALIAS_LEVELS[:nobody],
|
mentionable_level: Group::ALIAS_LEVELS[:nobody],
|
||||||
users: [ Fabricate(:user) ]
|
users: [Fabricate(:user)],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:post) { Fabricate(:post, raw: "mention @#{group.name}") }
|
let(:post) { Fabricate(:post, raw: "mention @#{group.name}") }
|
||||||
|
|
||||||
before do
|
before { Jobs.run_immediately! }
|
||||||
Jobs.run_immediately!
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'users can mention public groups, but does not create a notification' do
|
it "users can mention public groups, but does not create a notification" do
|
||||||
expect { post }.not_to change { Notification.where(notification_type: Notification.types[:group_mentioned]).count }
|
expect { post }.not_to change {
|
||||||
expect(post.cooked).to include("<a class=\"mention-group\" href=\"/groups/#{group.name}\">@#{group.name}</a>")
|
Notification.where(notification_type: Notification.types[:group_mentioned]).count
|
||||||
|
}
|
||||||
|
expect(post.cooked).to include(
|
||||||
|
"<a class=\"mention-group\" href=\"/groups/#{group.name}\">@#{group.name}</a>",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,27 +1,31 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'invalid requests', type: :request do
|
RSpec.describe "invalid requests", type: :request do
|
||||||
before do
|
before do
|
||||||
@orig_logger = Rails.logger
|
@orig_logger = Rails.logger
|
||||||
Rails.logger = @fake_logger = FakeLogger.new
|
Rails.logger = @fake_logger = FakeLogger.new
|
||||||
end
|
end
|
||||||
|
|
||||||
after do
|
after { Rails.logger = @orig_logger }
|
||||||
Rails.logger = @orig_logger
|
|
||||||
end
|
|
||||||
|
|
||||||
it "handles NotFound with invalid json body" do
|
it "handles NotFound with invalid json body" do
|
||||||
post "/latest.json", params: "{some: malformed: json", headers: { "content-type" => "application/json" }
|
post "/latest.json",
|
||||||
|
params: "{some: malformed: json",
|
||||||
|
headers: {
|
||||||
|
"content-type" => "application/json",
|
||||||
|
}
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
expect(@fake_logger.warnings.length).to eq(0)
|
expect(@fake_logger.warnings.length).to eq(0)
|
||||||
expect(@fake_logger.errors.length).to eq(0)
|
expect(@fake_logger.errors.length).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles EOFError when multipart request is malformed" do
|
it "handles EOFError when multipart request is malformed" do
|
||||||
post "/latest.json", params: "somecontent", headers: {
|
post "/latest.json",
|
||||||
"content-type" => "multipart/form-data; boundary=abcde",
|
params: "somecontent",
|
||||||
"content-length" => "1"
|
headers: {
|
||||||
}
|
"content-type" => "multipart/form-data; boundary=abcde",
|
||||||
|
"content-length" => "1",
|
||||||
|
}
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
expect(@fake_logger.warnings.length).to eq(0)
|
expect(@fake_logger.warnings.length).to eq(0)
|
||||||
expect(@fake_logger.errors.length).to eq(0)
|
expect(@fake_logger.errors.length).to eq(0)
|
||||||
|
@ -33,5 +37,4 @@ RSpec.describe 'invalid requests', type: :request do
|
||||||
expect(@fake_logger.warnings.length).to eq(0)
|
expect(@fake_logger.warnings.length).to eq(0)
|
||||||
expect(@fake_logger.errors.length).to eq(0)
|
expect(@fake_logger.errors.length).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,45 +1,46 @@
|
||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'invite only' do
|
RSpec.describe "invite only" do
|
||||||
|
describe "#create invite only" do
|
||||||
describe '#create invite only' do
|
it "can create user via API" do
|
||||||
it 'can create user via API' do
|
|
||||||
|
|
||||||
SiteSetting.invite_only = true
|
SiteSetting.invite_only = true
|
||||||
Jobs.run_immediately!
|
Jobs.run_immediately!
|
||||||
|
|
||||||
admin = Fabricate(:admin)
|
admin = Fabricate(:admin)
|
||||||
api_key = Fabricate(:api_key, user: admin)
|
api_key = Fabricate(:api_key, user: admin)
|
||||||
|
|
||||||
post '/users.json', params: {
|
post "/users.json",
|
||||||
name: 'bob',
|
params: {
|
||||||
username: 'bob',
|
name: "bob",
|
||||||
password: 'strongpassword',
|
username: "bob",
|
||||||
email: 'bob@bob.com',
|
password: "strongpassword",
|
||||||
}, headers: {
|
email: "bob@bob.com",
|
||||||
HTTP_API_KEY: api_key.key,
|
},
|
||||||
HTTP_API_USERNAME: admin.username
|
headers: {
|
||||||
}
|
HTTP_API_KEY: api_key.key,
|
||||||
|
HTTP_API_USERNAME: admin.username,
|
||||||
|
}
|
||||||
|
|
||||||
user_id = response.parsed_body["user_id"]
|
user_id = response.parsed_body["user_id"]
|
||||||
expect(user_id).to be > 0
|
expect(user_id).to be > 0
|
||||||
|
|
||||||
# activate and approve
|
# activate and approve
|
||||||
put "/admin/users/#{user_id}/activate.json", headers: {
|
put "/admin/users/#{user_id}/activate.json",
|
||||||
HTTP_API_KEY: api_key.key,
|
headers: {
|
||||||
HTTP_API_USERNAME: admin.username
|
HTTP_API_KEY: api_key.key,
|
||||||
}
|
HTTP_API_USERNAME: admin.username,
|
||||||
|
}
|
||||||
|
|
||||||
put "/admin/users/#{user_id}/approve.json", headers: {
|
put "/admin/users/#{user_id}/approve.json",
|
||||||
HTTP_API_KEY: api_key.key,
|
headers: {
|
||||||
HTTP_API_USERNAME: admin.username
|
HTTP_API_KEY: api_key.key,
|
||||||
}
|
HTTP_API_USERNAME: admin.username,
|
||||||
|
}
|
||||||
|
|
||||||
u = User.find(user_id)
|
u = User.find(user_id)
|
||||||
expect(u.active).to eq(true)
|
expect(u.active).to eq(true)
|
||||||
expect(u.approved).to eq(true)
|
expect(u.approved).to eq(true)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'message bus integration' do
|
RSpec.describe "message bus integration" do
|
||||||
|
|
||||||
it "allows anonymous requests to the messagebus" do
|
it "allows anonymous requests to the messagebus" do
|
||||||
post "/message-bus/poll"
|
post "/message-bus/poll"
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
@ -27,5 +26,4 @@ RSpec.describe 'message bus integration' do
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,25 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'multisite', type: [:multisite, :request] do
|
RSpec.describe "multisite", type: %i[multisite request] do
|
||||||
it "works" do
|
it "works" do
|
||||||
get "http://test.localhost/session/csrf.json"
|
get "http://test.localhost/session/csrf.json"
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
cookie = CGI.escape(response.cookies["_forum_session"])
|
cookie = CGI.escape(response.cookies["_forum_session"])
|
||||||
id1 = session["session_id"]
|
id1 = session["session_id"]
|
||||||
|
|
||||||
get "http://test.localhost/session/csrf.json", headers: { "Cookie" => "_forum_session=#{cookie};" }
|
get "http://test.localhost/session/csrf.json",
|
||||||
|
headers: {
|
||||||
|
"Cookie" => "_forum_session=#{cookie};",
|
||||||
|
}
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
id2 = session["session_id"]
|
id2 = session["session_id"]
|
||||||
|
|
||||||
expect(id1).to eq(id2)
|
expect(id1).to eq(id2)
|
||||||
|
|
||||||
get "http://test2.localhost/session/csrf.json", headers: { "Cookie" => "_forum_session=#{cookie};" }
|
get "http://test2.localhost/session/csrf.json",
|
||||||
|
headers: {
|
||||||
|
"Cookie" => "_forum_session=#{cookie};",
|
||||||
|
}
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
id3 = session["session_id"]
|
id3 = session["session_id"]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'multisite', type: [:multisite, :request] do
|
RSpec.describe "multisite", type: %i[multisite request] do
|
||||||
it "should always allow /srv/status through" do
|
it "should always allow /srv/status through" do
|
||||||
get "http://unknown.com/srv/status"
|
get "http://unknown.com/srv/status"
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
@ -13,10 +13,12 @@ RSpec.describe 'multisite', type: [:multisite, :request] do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should hit correct site otherwise" do
|
it "should hit correct site otherwise" do
|
||||||
site_1_url = Fabricate(:topic, title: "Site 1 Topic Title", user: Discourse.system_user).relative_url
|
site_1_url =
|
||||||
|
Fabricate(:topic, title: "Site 1 Topic Title", user: Discourse.system_user).relative_url
|
||||||
|
|
||||||
test_multisite_connection('second') do
|
test_multisite_connection("second") do
|
||||||
site_2_url = Fabricate(:topic, title: "Site 2 Topic Title", user: Discourse.system_user).relative_url
|
site_2_url =
|
||||||
|
Fabricate(:topic, title: "Site 2 Topic Title", user: Discourse.system_user).relative_url
|
||||||
|
|
||||||
get "http://test.localhost/#{site_1_url}.json"
|
get "http://test.localhost/#{site_1_url}.json"
|
||||||
expect(request.env["RAILS_MULTISITE_HOST"]).to eq("test.localhost")
|
expect(request.env["RAILS_MULTISITE_HOST"]).to eq("test.localhost")
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'rate limiter integration' do
|
RSpec.describe "rate limiter integration" do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
RateLimiter.enable
|
RateLimiter.enable
|
||||||
RateLimiter.clear_all!
|
RateLimiter.clear_all!
|
||||||
|
@ -13,12 +12,13 @@ RSpec.describe 'rate limiter integration' do
|
||||||
|
|
||||||
global_setting :reject_message_bus_queue_seconds, 0.1
|
global_setting :reject_message_bus_queue_seconds, 0.1
|
||||||
|
|
||||||
post "/message-bus/#{SecureRandom.hex}/poll", headers: {
|
post "/message-bus/#{SecureRandom.hex}/poll",
|
||||||
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.2}"
|
headers: {
|
||||||
}
|
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.2}",
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(429)
|
expect(response.status).to eq(429)
|
||||||
expect(response.headers['Retry-After'].to_i).to be > 29
|
expect(response.headers["Retry-After"].to_i).to be > 29
|
||||||
end
|
end
|
||||||
|
|
||||||
it "will not rate limit when all is good" do
|
it "will not rate limit when all is good" do
|
||||||
|
@ -26,9 +26,10 @@ RSpec.describe 'rate limiter integration' do
|
||||||
|
|
||||||
global_setting :reject_message_bus_queue_seconds, 0.1
|
global_setting :reject_message_bus_queue_seconds, 0.1
|
||||||
|
|
||||||
post "/message-bus/#{SecureRandom.hex}/poll", headers: {
|
post "/message-bus/#{SecureRandom.hex}/poll",
|
||||||
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.05}"
|
headers: {
|
||||||
}
|
"HTTP_X_REQUEST_START" => "t=#{Time.now.to_f - 0.05}",
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
@ -37,15 +38,15 @@ RSpec.describe 'rate limiter integration' do
|
||||||
name = Auth::DefaultCurrentUserProvider::TOKEN_COOKIE
|
name = Auth::DefaultCurrentUserProvider::TOKEN_COOKIE
|
||||||
|
|
||||||
# we try 11 times because the rate limit is 10
|
# we try 11 times because the rate limit is 10
|
||||||
11.times {
|
11.times do
|
||||||
cookies[name] = SecureRandom.hex
|
cookies[name] = SecureRandom.hex
|
||||||
get '/categories.json'
|
get "/categories.json"
|
||||||
expect(response.cookies.has_key?(name)).to eq(true)
|
expect(response.cookies.has_key?(name)).to eq(true)
|
||||||
expect(response.cookies[name]).to be_nil
|
expect(response.cookies[name]).to be_nil
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can cleanly limit requests and sets a Retry-After header' do
|
it "can cleanly limit requests and sets a Retry-After header" do
|
||||||
freeze_time
|
freeze_time
|
||||||
|
|
||||||
RateLimiter.clear_all!
|
RateLimiter.clear_all!
|
||||||
|
@ -55,17 +56,19 @@ RSpec.describe 'rate limiter integration' do
|
||||||
|
|
||||||
global_setting :max_admin_api_reqs_per_minute, 1
|
global_setting :max_admin_api_reqs_per_minute, 1
|
||||||
|
|
||||||
get '/admin/api/keys.json', headers: {
|
get "/admin/api/keys.json",
|
||||||
HTTP_API_KEY: api_key.key,
|
headers: {
|
||||||
HTTP_API_USERNAME: admin.username
|
HTTP_API_KEY: api_key.key,
|
||||||
}
|
HTTP_API_USERNAME: admin.username,
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
get '/admin/api/keys.json', headers: {
|
get "/admin/api/keys.json",
|
||||||
HTTP_API_KEY: api_key.key,
|
headers: {
|
||||||
HTTP_API_USERNAME: admin.username
|
HTTP_API_KEY: api_key.key,
|
||||||
}
|
HTTP_API_USERNAME: admin.username,
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(429)
|
expect(response.status).to eq(429)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'request tracker' do
|
RSpec.describe "request tracker" do
|
||||||
let(:api_key) do
|
let(:api_key) do
|
||||||
Fabricate(
|
Fabricate(
|
||||||
:api_key,
|
:api_key,
|
||||||
user: Fabricate.build(:user),
|
user: Fabricate.build(:user),
|
||||||
api_key_scopes: [ApiKeyScope.new(resource: 'users', action: 'show')]
|
api_key_scopes: [ApiKeyScope.new(resource: "users", action: "show")],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:user_api_key) do
|
let(:user_api_key) do
|
||||||
Fabricate(:user_api_key, scopes: [Fabricate.build(:user_api_key_scope, name: 'session_info')])
|
Fabricate(:user_api_key, scopes: [Fabricate.build(:user_api_key_scope, name: "session_info")])
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -25,8 +25,8 @@ RSpec.describe 'request tracker' do
|
||||||
CachedCounting.disable
|
CachedCounting.disable
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when using an api key' do
|
context "when using an api key" do
|
||||||
it 'is counted as an API request' do
|
it "is counted as an API request" do
|
||||||
get "/u/#{api_key.user.username}.json", headers: { HTTP_API_KEY: api_key.key }
|
get "/u/#{api_key.user.username}.json", headers: { HTTP_API_KEY: api_key.key }
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ RSpec.describe 'request tracker' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when using an user api key' do
|
context "when using an user api key" do
|
||||||
it 'is counted as a user API request' do
|
it "is counted as a user API request" do
|
||||||
get '/session/current.json', headers: { HTTP_USER_API_KEY: user_api_key.key }
|
get "/session/current.json", headers: { HTTP_USER_API_KEY: user_api_key.key }
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
CachedCounting.flush
|
CachedCounting.flush
|
||||||
|
|
|
@ -2,45 +2,41 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe "spammers on same IP" do
|
RSpec.describe "spammers on same IP" do
|
||||||
let(:ip_address) { '182.189.119.174' }
|
let(:ip_address) { "182.189.119.174" }
|
||||||
let!(:spammer1) { Fabricate(:user, ip_address: ip_address) }
|
let!(:spammer1) { Fabricate(:user, ip_address: ip_address) }
|
||||||
let!(:spammer2) { Fabricate(:user, ip_address: ip_address) }
|
let!(:spammer2) { Fabricate(:user, ip_address: ip_address) }
|
||||||
let(:spammer3) { Fabricate(:user, ip_address: ip_address) }
|
let(:spammer3) { Fabricate(:user, ip_address: ip_address) }
|
||||||
|
|
||||||
context 'when flag_sockpuppets is disabled' do
|
context "when flag_sockpuppets is disabled" do
|
||||||
let!(:first_post) { create_post(user: spammer1) }
|
let!(:first_post) { create_post(user: spammer1) }
|
||||||
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
|
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
|
||||||
|
|
||||||
it 'should not increase spam count' do
|
it "should not increase spam count" do
|
||||||
expect(first_post.reload.spam_count).to eq(0)
|
expect(first_post.reload.spam_count).to eq(0)
|
||||||
expect(second_post.reload.spam_count).to eq(0)
|
expect(second_post.reload.spam_count).to eq(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when flag_sockpuppets is enabled' do
|
context "when flag_sockpuppets is enabled" do
|
||||||
before do
|
before { SiteSetting.flag_sockpuppets = true }
|
||||||
SiteSetting.flag_sockpuppets = true
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
after { SiteSetting.flag_sockpuppets = false }
|
||||||
SiteSetting.flag_sockpuppets = false
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when first spammer starts a topic' do
|
context "when first spammer starts a topic" do
|
||||||
let!(:first_post) { create_post(user: spammer1) }
|
let!(:first_post) { create_post(user: spammer1) }
|
||||||
|
|
||||||
context 'when second spammer replies' do
|
context "when second spammer replies" do
|
||||||
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
|
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
|
||||||
|
|
||||||
it 'should increase spam count' do
|
it "should increase spam count" do
|
||||||
expect(first_post.reload.spam_count).to eq(1)
|
expect(first_post.reload.spam_count).to eq(1)
|
||||||
expect(second_post.reload.spam_count).to eq(1)
|
expect(second_post.reload.spam_count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with third spam post' do
|
context "with third spam post" do
|
||||||
let!(:third_post) { create_post(user: spammer3, topic: first_post.topic) }
|
let!(:third_post) { create_post(user: spammer3, topic: first_post.topic) }
|
||||||
|
|
||||||
it 'should increase spam count' do
|
it "should increase spam count" do
|
||||||
expect(first_post.reload.spam_count).to eq(1)
|
expect(first_post.reload.spam_count).to eq(1)
|
||||||
expect(second_post.reload.spam_count).to eq(1)
|
expect(second_post.reload.spam_count).to eq(1)
|
||||||
expect(third_post.reload.spam_count).to eq(1)
|
expect(third_post.reload.spam_count).to eq(1)
|
||||||
|
@ -49,16 +45,18 @@ RSpec.describe "spammers on same IP" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when first user is not new' do
|
context "when first user is not new" do
|
||||||
let!(:old_user) { Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago, trust_level: TrustLevel[1]) }
|
let!(:old_user) do
|
||||||
|
Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago, trust_level: TrustLevel[1])
|
||||||
|
end
|
||||||
|
|
||||||
context 'when first user starts a topic' do
|
context "when first user starts a topic" do
|
||||||
let!(:first_post) { create_post(user: old_user) }
|
let!(:first_post) { create_post(user: old_user) }
|
||||||
|
|
||||||
context 'with a reply by a new user at the same IP address' do
|
context "with a reply by a new user at the same IP address" do
|
||||||
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
|
let!(:second_post) { create_post(user: spammer2, topic: first_post.topic) }
|
||||||
|
|
||||||
it 'should increase the spam count correctly' do
|
it "should increase the spam count correctly" do
|
||||||
expect(first_post.reload.spam_count).to eq(0)
|
expect(first_post.reload.spam_count).to eq(0)
|
||||||
expect(second_post.reload.spam_count).to eq(1)
|
expect(second_post.reload.spam_count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe "spam rules for users" do
|
RSpec.describe "spam rules for users" do
|
||||||
describe 'auto-silence users based on flagging' do
|
describe "auto-silence users based on flagging" do
|
||||||
fab!(:admin) { Fabricate(:admin) } # needed to send a system message
|
fab!(:admin) { Fabricate(:admin) } # needed to send a system message
|
||||||
fab!(:moderator) { Fabricate(:moderator) }
|
fab!(:moderator) { Fabricate(:moderator) }
|
||||||
fab!(:user1) { Fabricate(:user) }
|
fab!(:user1) { Fabricate(:user) }
|
||||||
fab!(:user2) { Fabricate(:user) }
|
fab!(:user2) { Fabricate(:user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:disabled]
|
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:disabled]
|
||||||
|
@ -15,23 +15,21 @@ RSpec.describe "spam rules for users" do
|
||||||
SiteSetting.num_users_to_silence_new_user = 2
|
SiteSetting.num_users_to_silence_new_user = 2
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when spammer is a new user' do
|
context "when spammer is a new user" do
|
||||||
fab!(:spammer) { Fabricate(:user, trust_level: TrustLevel[0]) }
|
fab!(:spammer) { Fabricate(:user, trust_level: TrustLevel[0]) }
|
||||||
|
|
||||||
context 'when spammer post is not flagged enough times' do
|
context "when spammer post is not flagged enough times" do
|
||||||
let!(:spam_post) { create_post(user: spammer) }
|
let!(:spam_post) { create_post(user: spammer) }
|
||||||
let!(:spam_post2) { create_post(user: spammer) }
|
let!(:spam_post2) { create_post(user: spammer) }
|
||||||
|
|
||||||
before do
|
before { PostActionCreator.create(user1, spam_post, :spam) }
|
||||||
PostActionCreator.create(user1, spam_post, :spam)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should not hide the post' do
|
it "should not hide the post" do
|
||||||
expect(spam_post.reload).to_not be_hidden
|
expect(spam_post.reload).to_not be_hidden
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when spam posts are flagged enough times, but not by enough users' do
|
context "when spam posts are flagged enough times, but not by enough users" do
|
||||||
it 'should not hide the post' do
|
it "should not hide the post" do
|
||||||
PostActionCreator.create(user1, spam_post2, :spam)
|
PostActionCreator.create(user1, spam_post2, :spam)
|
||||||
|
|
||||||
expect(spam_post.reload).to_not be_hidden
|
expect(spam_post.reload).to_not be_hidden
|
||||||
|
@ -40,16 +38,30 @@ RSpec.describe "spam rules for users" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when one spam post is flagged enough times by enough users' do
|
context "when one spam post is flagged enough times by enough users" do
|
||||||
fab!(:another_topic) { Fabricate(:topic) }
|
fab!(:another_topic) { Fabricate(:topic) }
|
||||||
let!(:private_messages_count) { spammer.private_topics_count }
|
let!(:private_messages_count) { spammer.private_topics_count }
|
||||||
let!(:mod_pm_count) { moderator.private_topics_count }
|
let!(:mod_pm_count) { moderator.private_topics_count }
|
||||||
let!(:reviewable) { PostActionCreator.spam(user2, spam_post).reviewable }
|
let!(:reviewable) { PostActionCreator.spam(user2, spam_post).reviewable }
|
||||||
|
|
||||||
it 'should hide the posts' do
|
it "should hide the posts" do
|
||||||
expect(Guardian.new(spammer).can_create_topic?(nil)).to be(false)
|
expect(Guardian.new(spammer).can_create_topic?(nil)).to be(false)
|
||||||
expect { PostCreator.create(spammer, title: 'limited time offer for you', raw: 'better buy this stuff ok', archetype_id: 1) }.to raise_error(Discourse::InvalidAccess)
|
expect {
|
||||||
expect(PostCreator.create(spammer, topic_id: another_topic.id, raw: 'my reply is spam in your topic', archetype_id: 1)).to eq(nil)
|
PostCreator.create(
|
||||||
|
spammer,
|
||||||
|
title: "limited time offer for you",
|
||||||
|
raw: "better buy this stuff ok",
|
||||||
|
archetype_id: 1,
|
||||||
|
)
|
||||||
|
}.to raise_error(Discourse::InvalidAccess)
|
||||||
|
expect(
|
||||||
|
PostCreator.create(
|
||||||
|
spammer,
|
||||||
|
topic_id: another_topic.id,
|
||||||
|
raw: "my reply is spam in your topic",
|
||||||
|
archetype_id: 1,
|
||||||
|
),
|
||||||
|
).to eq(nil)
|
||||||
expect(spammer.reload).to be_silenced
|
expect(spammer.reload).to be_silenced
|
||||||
expect(spam_post.reload).to be_hidden
|
expect(spam_post.reload).to be_hidden
|
||||||
expect(spam_post2.reload).to be_hidden
|
expect(spam_post2.reload).to be_hidden
|
||||||
|
@ -57,22 +69,24 @@ RSpec.describe "spam rules for users" do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when a post is deleted" do
|
context "when a post is deleted" do
|
||||||
it 'should silence the spammer' do
|
it "should silence the spammer" do
|
||||||
spam_post.trash!(moderator); spammer.reload
|
spam_post.trash!(moderator)
|
||||||
|
spammer.reload
|
||||||
expect(spammer.reload).to be_silenced
|
expect(spammer.reload).to be_silenced
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when spammer becomes trust level 1" do
|
context "when spammer becomes trust level 1" do
|
||||||
it 'should silence the spammer' do
|
it "should silence the spammer" do
|
||||||
spammer.change_trust_level!(TrustLevel[1]); spammer.reload
|
spammer.change_trust_level!(TrustLevel[1])
|
||||||
|
spammer.reload
|
||||||
expect(spammer.reload).to be_silenced
|
expect(spammer.reload).to be_silenced
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with hide_post_sensitivity' do
|
context "with hide_post_sensitivity" do
|
||||||
it 'should silence the spammer' do
|
it "should silence the spammer" do
|
||||||
Reviewable.set_priorities(high: 2.0)
|
Reviewable.set_priorities(high: 2.0)
|
||||||
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low]
|
SiteSetting.hide_post_sensitivity = Reviewable.sensitivities[:low]
|
||||||
PostActionCreator.create(user2, spam_post, :spam)
|
PostActionCreator.create(user2, spam_post, :spam)
|
||||||
|
@ -84,19 +98,26 @@ RSpec.describe "spam rules for users" do
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when spammer has trust level basic" do
|
context "when spammer has trust level basic" do
|
||||||
let(:spammer) { Fabricate(:user, trust_level: TrustLevel[1]) }
|
let(:spammer) { Fabricate(:user, trust_level: TrustLevel[1]) }
|
||||||
|
|
||||||
context 'when one spam post is flagged enough times by enough users' do
|
context "when one spam post is flagged enough times by enough users" do
|
||||||
let!(:spam_post) { Fabricate(:post, user: spammer) }
|
let!(:spam_post) { Fabricate(:post, user: spammer) }
|
||||||
let!(:private_messages_count) { spammer.private_topics_count }
|
let!(:private_messages_count) { spammer.private_topics_count }
|
||||||
|
|
||||||
it 'should not allow spammer to create new posts' do
|
it "should not allow spammer to create new posts" do
|
||||||
PostActionCreator.create(user1, spam_post, :spam)
|
PostActionCreator.create(user1, spam_post, :spam)
|
||||||
PostActionCreator.create(user2, spam_post, :spam)
|
PostActionCreator.create(user2, spam_post, :spam)
|
||||||
|
|
||||||
expect(spam_post.reload).to_not be_hidden
|
expect(spam_post.reload).to_not be_hidden
|
||||||
expect(Guardian.new(spammer).can_create_topic?(nil)).to be(true)
|
expect(Guardian.new(spammer).can_create_topic?(nil)).to be(true)
|
||||||
expect { PostCreator.create(spammer, title: 'limited time offer for you', raw: 'better buy this stuff ok', archetype_id: 1) }.to_not raise_error
|
expect {
|
||||||
|
PostCreator.create(
|
||||||
|
spammer,
|
||||||
|
title: "limited time offer for you",
|
||||||
|
raw: "better buy this stuff ok",
|
||||||
|
archetype_id: 1,
|
||||||
|
)
|
||||||
|
}.to_not raise_error
|
||||||
expect(spammer.reload.private_topics_count).to eq(private_messages_count)
|
expect(spammer.reload.private_topics_count).to eq(private_messages_count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -104,11 +125,11 @@ RSpec.describe "spam rules for users" do
|
||||||
|
|
||||||
[[:user, trust_level: TrustLevel[2]], [:admin], [:moderator]].each do |spammer_args|
|
[[:user, trust_level: TrustLevel[2]], [:admin], [:moderator]].each do |spammer_args|
|
||||||
context "spammer is trusted #{spammer_args[0]}" do
|
context "spammer is trusted #{spammer_args[0]}" do
|
||||||
let!(:spammer) { Fabricate(*spammer_args) }
|
let!(:spammer) { Fabricate(*spammer_args) }
|
||||||
let!(:spam_post) { Fabricate(:post, user: spammer) }
|
let!(:spam_post) { Fabricate(:post, user: spammer) }
|
||||||
let!(:private_messages_count) { spammer.private_topics_count }
|
let!(:private_messages_count) { spammer.private_topics_count }
|
||||||
|
|
||||||
it 'should not hide the post' do
|
it "should not hide the post" do
|
||||||
PostActionCreator.create(user1, spam_post, :spam)
|
PostActionCreator.create(user1, spam_post, :spam)
|
||||||
PostActionCreator.create(user2, spam_post, :spam)
|
PostActionCreator.create(user2, spam_post, :spam)
|
||||||
|
|
||||||
|
|
|
@ -4,36 +4,34 @@
|
||||||
RSpec.describe Topic do
|
RSpec.describe Topic do
|
||||||
let(:job_klass) { Jobs::CloseTopic }
|
let(:job_klass) { Jobs::CloseTopic }
|
||||||
|
|
||||||
context 'when creating a topic without auto-close' do
|
context "when creating a topic without auto-close" do
|
||||||
let(:topic) { Fabricate(:topic, category: category) }
|
let(:topic) { Fabricate(:topic, category: category) }
|
||||||
|
|
||||||
context 'when uncategorized' do
|
context "when uncategorized" do
|
||||||
let(:category) { nil }
|
let(:category) { nil }
|
||||||
|
|
||||||
it 'should not schedule the topic to auto-close' do
|
it "should not schedule the topic to auto-close" do
|
||||||
expect(topic.public_topic_timer).to eq(nil)
|
expect(topic.public_topic_timer).to eq(nil)
|
||||||
expect(job_klass.jobs).to eq([])
|
expect(job_klass.jobs).to eq([])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with category without default auto-close' do
|
context "with category without default auto-close" do
|
||||||
let(:category) { Fabricate(:category, auto_close_hours: nil) }
|
let(:category) { Fabricate(:category, auto_close_hours: nil) }
|
||||||
|
|
||||||
it 'should not schedule the topic to auto-close' do
|
it "should not schedule the topic to auto-close" do
|
||||||
expect(topic.public_topic_timer).to eq(nil)
|
expect(topic.public_topic_timer).to eq(nil)
|
||||||
expect(job_klass.jobs).to eq([])
|
expect(job_klass.jobs).to eq([])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when jobs may be queued' do
|
context "when jobs may be queued" do
|
||||||
before do
|
before { freeze_time }
|
||||||
freeze_time
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when category has a default auto-close' do
|
context "when category has a default auto-close" do
|
||||||
let(:category) { Fabricate(:category, auto_close_hours: 2.0) }
|
let(:category) { Fabricate(:category, auto_close_hours: 2.0) }
|
||||||
|
|
||||||
it 'should schedule the topic to auto-close' do
|
it "should schedule the topic to auto-close" do
|
||||||
topic
|
topic
|
||||||
|
|
||||||
topic_status_update = TopicTimer.last
|
topic_status_update = TopicTimer.last
|
||||||
|
@ -42,11 +40,11 @@ RSpec.describe Topic do
|
||||||
expect(topic.public_topic_timer.execute_at).to be_within_one_second_of(2.hours.from_now)
|
expect(topic.public_topic_timer.execute_at).to be_within_one_second_of(2.hours.from_now)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when topic was created by staff user' do
|
context "when topic was created by staff user" do
|
||||||
let(:admin) { Fabricate(:admin) }
|
let(:admin) { Fabricate(:admin) }
|
||||||
let(:staff_topic) { Fabricate(:topic, user: admin, category: category) }
|
let(:staff_topic) { Fabricate(:topic, user: admin, category: category) }
|
||||||
|
|
||||||
it 'should schedule the topic to auto-close' do
|
it "should schedule the topic to auto-close" do
|
||||||
staff_topic
|
staff_topic
|
||||||
|
|
||||||
topic_status_update = TopicTimer.last
|
topic_status_update = TopicTimer.last
|
||||||
|
@ -56,23 +54,24 @@ RSpec.describe Topic do
|
||||||
expect(topic_status_update.user).to eq(Discourse.system_user)
|
expect(topic_status_update.user).to eq(Discourse.system_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when topic is closed manually' do
|
context "when topic is closed manually" do
|
||||||
it 'should remove the schedule to auto-close the topic' do
|
it "should remove the schedule to auto-close the topic" do
|
||||||
topic_timer_id = staff_topic.public_topic_timer.id
|
topic_timer_id = staff_topic.public_topic_timer.id
|
||||||
|
|
||||||
staff_topic.update_status('closed', true, admin)
|
staff_topic.update_status("closed", true, admin)
|
||||||
|
|
||||||
expect(TopicTimer.with_deleted.find(topic_timer_id).deleted_at)
|
expect(
|
||||||
.to be_within_one_second_of(Time.zone.now)
|
TopicTimer.with_deleted.find(topic_timer_id).deleted_at,
|
||||||
|
).to be_within_one_second_of(Time.zone.now)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when topic was created by a non-staff user' do
|
context "when topic was created by a non-staff user" do
|
||||||
let(:regular_user) { Fabricate(:user) }
|
let(:regular_user) { Fabricate(:user) }
|
||||||
let(:regular_user_topic) { Fabricate(:topic, user: regular_user, category: category) }
|
let(:regular_user_topic) { Fabricate(:topic, user: regular_user, category: category) }
|
||||||
|
|
||||||
it 'should schedule the topic to auto-close' do
|
it "should schedule the topic to auto-close" do
|
||||||
regular_user_topic
|
regular_user_topic
|
||||||
|
|
||||||
topic_status_update = TopicTimer.last
|
topic_status_update = TopicTimer.last
|
||||||
|
|
|
@ -9,11 +9,11 @@ RSpec.describe "Topic Thumbnails" do
|
||||||
fab!(:topic) { Fabricate(:topic, image_upload_id: image.id) }
|
fab!(:topic) { Fabricate(:topic, image_upload_id: image.id) }
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
describe 'latest' do
|
describe "latest" do
|
||||||
def get_topic
|
def get_topic
|
||||||
Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes))
|
Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes))
|
||||||
Discourse.redis.del(topic.thumbnail_job_redis_key([]))
|
Discourse.redis.del(topic.thumbnail_job_redis_key([]))
|
||||||
get '/latest.json'
|
get "/latest.json"
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
response.parsed_body["topic_list"]["topics"][0]
|
response.parsed_body["topic_list"]["topics"][0]
|
||||||
end
|
end
|
||||||
|
@ -27,11 +27,7 @@ RSpec.describe "Topic Thumbnails" do
|
||||||
context "with a theme" do
|
context "with a theme" do
|
||||||
before do
|
before do
|
||||||
theme = Fabricate(:theme)
|
theme = Fabricate(:theme)
|
||||||
theme.theme_modifier_set.topic_thumbnail_sizes = [
|
theme.theme_modifier_set.topic_thumbnail_sizes = [[10, 10], [20, 20], [30, 30]]
|
||||||
[10, 10],
|
|
||||||
[20, 20],
|
|
||||||
[30, 30]
|
|
||||||
]
|
|
||||||
theme.theme_modifier_set.save!
|
theme.theme_modifier_set.save!
|
||||||
theme.set_default!
|
theme.set_default!
|
||||||
end
|
end
|
||||||
|
@ -39,15 +35,15 @@ RSpec.describe "Topic Thumbnails" do
|
||||||
it "includes the theme specified resolutions" do
|
it "includes the theme specified resolutions" do
|
||||||
topic_json = nil
|
topic_json = nil
|
||||||
|
|
||||||
expect do
|
expect do topic_json = get_topic end.to change {
|
||||||
topic_json = get_topic
|
Jobs::GenerateTopicThumbnails.jobs.size
|
||||||
end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(2)
|
}.by(2)
|
||||||
|
|
||||||
expect(
|
expect(Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] }).to eq(
|
||||||
Jobs::GenerateTopicThumbnails.jobs.map { |j| j["args"][0]["extra_sizes"] }
|
[
|
||||||
).to eq([
|
nil, # Job for core/plugin sizes
|
||||||
nil, # Job for core/plugin sizes
|
[[10, 10], [20, 20], [30, 30]],
|
||||||
[[10, 10], [20, 20], [30, 30]]] # Job for theme sizes
|
], # Job for theme sizes
|
||||||
)
|
)
|
||||||
|
|
||||||
thumbnails = topic_json["thumbnails"]
|
thumbnails = topic_json["thumbnails"]
|
||||||
|
@ -67,9 +63,9 @@ RSpec.describe "Topic Thumbnails" do
|
||||||
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
|
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
|
||||||
|
|
||||||
# Request again
|
# Request again
|
||||||
expect do
|
expect do topic_json = get_topic end.not_to change {
|
||||||
topic_json = get_topic
|
Jobs::GenerateTopicThumbnails.jobs.size
|
||||||
end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size }
|
}
|
||||||
|
|
||||||
thumbnails = topic_json["thumbnails"]
|
thumbnails = topic_json["thumbnails"]
|
||||||
|
|
||||||
|
@ -82,7 +78,6 @@ RSpec.describe "Topic Thumbnails" do
|
||||||
expect(thumbnails[1]["width"]).to eq(9)
|
expect(thumbnails[1]["width"]).to eq(9)
|
||||||
expect(thumbnails[1]["height"]).to eq(9)
|
expect(thumbnails[1]["height"]).to eq(9)
|
||||||
expect(thumbnails[1]["url"]).to include("/optimized/")
|
expect(thumbnails[1]["url"]).to include("/optimized/")
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -92,25 +87,23 @@ RSpec.describe "Topic Thumbnails" do
|
||||||
plugin.register_topic_thumbnail_size [512, 512]
|
plugin.register_topic_thumbnail_size [512, 512]
|
||||||
end
|
end
|
||||||
|
|
||||||
after do
|
after { DiscoursePluginRegistry.reset! }
|
||||||
DiscoursePluginRegistry.reset!
|
|
||||||
end
|
|
||||||
|
|
||||||
it "includes the theme specified resolutions" do
|
it "includes the theme specified resolutions" do
|
||||||
topic_json = nil
|
topic_json = nil
|
||||||
|
|
||||||
expect do
|
expect do topic_json = get_topic end.to change {
|
||||||
topic_json = get_topic
|
Jobs::GenerateTopicThumbnails.jobs.size
|
||||||
end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(1)
|
}.by(1)
|
||||||
|
|
||||||
# Run the job
|
# Run the job
|
||||||
args = Jobs::GenerateTopicThumbnails.jobs.last["args"].first
|
args = Jobs::GenerateTopicThumbnails.jobs.last["args"].first
|
||||||
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
|
Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access)
|
||||||
|
|
||||||
# Request again
|
# Request again
|
||||||
expect do
|
expect do topic_json = get_topic end.not_to change {
|
||||||
topic_json = get_topic
|
Jobs::GenerateTopicThumbnails.jobs.size
|
||||||
end.not_to change { Jobs::GenerateTopicThumbnails.jobs.size }
|
}
|
||||||
|
|
||||||
thumbnails = topic_json["thumbnails"]
|
thumbnails = topic_json["thumbnails"]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
describe 'Twitter OAuth 1.0a' do
|
describe "Twitter OAuth 1.0a" do
|
||||||
let(:access_token) { "twitter_access_token_448" }
|
let(:access_token) { "twitter_access_token_448" }
|
||||||
let(:consumer_key) { "abcdef11223344" }
|
let(:consumer_key) { "abcdef11223344" }
|
||||||
let(:consumer_secret) { "adddcccdddd99922" }
|
let(:consumer_secret) { "adddcccdddd99922" }
|
||||||
|
@ -14,14 +14,15 @@ describe 'Twitter OAuth 1.0a' do
|
||||||
created_at: "Sat May 09 17:58:22 +0000 2009",
|
created_at: "Sat May 09 17:58:22 +0000 2009",
|
||||||
default_profile: false,
|
default_profile: false,
|
||||||
default_profile_image: false,
|
default_profile_image: false,
|
||||||
description: "I taught your phone that thing you like. The Mobile Partner Engineer @Twitter. ",
|
description:
|
||||||
|
"I taught your phone that thing you like. The Mobile Partner Engineer @Twitter. ",
|
||||||
favourites_count: 588,
|
favourites_count: 588,
|
||||||
follow_request_sent: nil,
|
follow_request_sent: nil,
|
||||||
followers_count: 10625,
|
followers_count: 10_625,
|
||||||
following: nil,
|
following: nil,
|
||||||
friends_count: 1181,
|
friends_count: 1181,
|
||||||
geo_enabled: true,
|
geo_enabled: true,
|
||||||
id: 38895958,
|
id: 38_895_958,
|
||||||
id_str: "38895958",
|
id_str: "38895958",
|
||||||
is_translator: false,
|
is_translator: false,
|
||||||
lang: "en",
|
lang: "en",
|
||||||
|
@ -30,11 +31,14 @@ describe 'Twitter OAuth 1.0a' do
|
||||||
name: "Sean Cook",
|
name: "Sean Cook",
|
||||||
notifications: nil,
|
notifications: nil,
|
||||||
profile_background_color: "1A1B1F",
|
profile_background_color: "1A1B1F",
|
||||||
profile_background_image_url: "http://a0.twimg.com/profile_background_images/495742332/purty_wood.png",
|
profile_background_image_url:
|
||||||
profile_background_image_url_https: "https://si0.twimg.com/profile_background_images/495742332/purty_wood.png",
|
"http://a0.twimg.com/profile_background_images/495742332/purty_wood.png",
|
||||||
|
profile_background_image_url_https:
|
||||||
|
"https://si0.twimg.com/profile_background_images/495742332/purty_wood.png",
|
||||||
profile_background_tile: true,
|
profile_background_tile: true,
|
||||||
profile_image_url: "http://a0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG",
|
profile_image_url: "http://a0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG",
|
||||||
profile_image_url_https: "https://si0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG",
|
profile_image_url_https:
|
||||||
|
"https://si0.twimg.com/profile_images/1751506047/dead_sexy_normal.JPG",
|
||||||
profile_link_color: "2FC2EF",
|
profile_link_color: "2FC2EF",
|
||||||
profile_sidebar_border_color: "181A1E",
|
profile_sidebar_border_color: "181A1E",
|
||||||
profile_sidebar_fill_color: "252429",
|
profile_sidebar_fill_color: "252429",
|
||||||
|
@ -46,19 +50,17 @@ describe 'Twitter OAuth 1.0a' do
|
||||||
statuses_count: 2609,
|
statuses_count: 2609,
|
||||||
time_zone: "Pacific Time (US & Canada)",
|
time_zone: "Pacific Time (US & Canada)",
|
||||||
url: nil,
|
url: nil,
|
||||||
utc_offset: -28800,
|
utc_offset: -28_800,
|
||||||
verified: true,
|
verified: true,
|
||||||
email: email
|
email: email,
|
||||||
}
|
}
|
||||||
stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json")
|
stub_request(:get, "https://api.twitter.com/1.1/account/verify_credentials.json").with(
|
||||||
.with(
|
query: {
|
||||||
query: {
|
include_email: true,
|
||||||
include_email: true,
|
include_entities: false,
|
||||||
include_entities: false,
|
skip_status: true,
|
||||||
skip_status: true
|
},
|
||||||
}
|
).to_return(status: 200, body: JSON.dump(body))
|
||||||
)
|
|
||||||
.to_return(status: 200, body: JSON.dump(body))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -66,28 +68,28 @@ describe 'Twitter OAuth 1.0a' do
|
||||||
SiteSetting.twitter_consumer_key = consumer_key
|
SiteSetting.twitter_consumer_key = consumer_key
|
||||||
SiteSetting.twitter_consumer_secret = consumer_secret
|
SiteSetting.twitter_consumer_secret = consumer_secret
|
||||||
|
|
||||||
stub_request(:post, "https://api.twitter.com/oauth/request_token")
|
stub_request(:post, "https://api.twitter.com/oauth/request_token").to_return(
|
||||||
.to_return(
|
status: 200,
|
||||||
status: 200,
|
body:
|
||||||
body: Rack::Utils.build_query(
|
Rack::Utils.build_query(
|
||||||
oauth_token: access_token,
|
oauth_token: access_token,
|
||||||
oauth_token_secret: oauth_token_secret,
|
oauth_token_secret: oauth_token_secret,
|
||||||
oauth_callback_confirmed: true
|
oauth_callback_confirmed: true,
|
||||||
),
|
),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type" => "application/x-www-form-urlencoded"
|
"Content-Type" => "application/x-www-form-urlencoded",
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
stub_request(:post, "https://api.twitter.com/oauth/access_token")
|
stub_request(:post, "https://api.twitter.com/oauth/access_token").to_return(
|
||||||
.to_return(
|
status: 200,
|
||||||
status: 200,
|
body:
|
||||||
body: Rack::Utils.build_query(
|
Rack::Utils.build_query(
|
||||||
oauth_token: access_token,
|
oauth_token: access_token,
|
||||||
oauth_token_secret: oauth_token_secret,
|
oauth_token_secret: oauth_token_secret,
|
||||||
user_id: "43423432422",
|
user_id: "43423432422",
|
||||||
screen_name: "twitterapi"
|
screen_name: "twitterapi",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "signs in the user if the API response from twitter includes an email (implies it's verified) and the email matches an existing user's" do
|
it "signs in the user if the API response from twitter includes an email (implies it's verified) and the email matches an existing user's" do
|
||||||
|
|
|
@ -8,80 +8,121 @@ RSpec.describe WatchedWord do
|
||||||
fab!(:topic) { Fabricate(:topic) }
|
fab!(:topic) { Fabricate(:topic) }
|
||||||
fab!(:first_post) { Fabricate(:post, topic: topic) }
|
fab!(:first_post) { Fabricate(:post, topic: topic) }
|
||||||
|
|
||||||
let(:require_approval_word) { Fabricate(:watched_word, action: WatchedWord.actions[:require_approval]) }
|
let(:require_approval_word) do
|
||||||
|
Fabricate(:watched_word, action: WatchedWord.actions[:require_approval])
|
||||||
|
end
|
||||||
let(:flag_word) { Fabricate(:watched_word, action: WatchedWord.actions[:flag]) }
|
let(:flag_word) { Fabricate(:watched_word, action: WatchedWord.actions[:flag]) }
|
||||||
let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
|
let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
|
||||||
let(:another_block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
|
let(:another_block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) }
|
||||||
|
|
||||||
before_all do
|
before_all { WordWatcher.clear_cache! }
|
||||||
WordWatcher.clear_cache!
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
after { WordWatcher.clear_cache! }
|
||||||
WordWatcher.clear_cache!
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with block" do
|
context "with block" do
|
||||||
def should_block_post(manager)
|
def should_block_post(manager)
|
||||||
expect {
|
expect {
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result).to_not be_success
|
expect(result).to_not be_success
|
||||||
expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_word', word: block_word.word))
|
expect(result.errors[:base]&.first).to eq(
|
||||||
|
I18n.t("contains_blocked_word", word: block_word.word),
|
||||||
|
)
|
||||||
}.to_not change { Post.count }
|
}.to_not change { Post.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
it "escapes the blocked word in error message" do
|
it "escapes the blocked word in error message" do
|
||||||
block_word = Fabricate(:watched_word, action: WatchedWord.actions[:block], word: "<a>")
|
block_word = Fabricate(:watched_word, action: WatchedWord.actions[:block], word: "<a>")
|
||||||
manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
tl2_user,
|
||||||
|
raw: "Want some #{block_word.word} for cheap?",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result).to_not be_success
|
expect(result).to_not be_success
|
||||||
expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_word', word: "<a>"))
|
expect(result.errors[:base]&.first).to eq(I18n.t("contains_blocked_word", word: "<a>"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should prevent the post from being created" do
|
it "should prevent the post from being created" do
|
||||||
manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
tl2_user,
|
||||||
|
raw: "Want some #{block_word.word} for cheap?",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
should_block_post(manager)
|
should_block_post(manager)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "look at title too" do
|
it "look at title too" do
|
||||||
manager = NewPostManager.new(tl2_user, title: "We sell #{block_word.word} online", raw: "Want some poutine for cheap?", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
tl2_user,
|
||||||
|
title: "We sell #{block_word.word} online",
|
||||||
|
raw: "Want some poutine for cheap?",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
should_block_post(manager)
|
should_block_post(manager)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should block the post from admin" do
|
it "should block the post from admin" do
|
||||||
manager = NewPostManager.new(admin, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
admin,
|
||||||
|
raw: "Want some #{block_word.word} for cheap?",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
should_block_post(manager)
|
should_block_post(manager)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should block the post from moderator" do
|
it "should block the post from moderator" do
|
||||||
manager = NewPostManager.new(moderator, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
moderator,
|
||||||
|
raw: "Want some #{block_word.word} for cheap?",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
should_block_post(manager)
|
should_block_post(manager)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should block the post if it contains multiple blocked words" do
|
it "should block the post if it contains multiple blocked words" do
|
||||||
manager = NewPostManager.new(moderator, raw: "Want some #{block_word.word} #{another_block_word.word} for cheap?", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
moderator,
|
||||||
|
raw: "Want some #{block_word.word} #{another_block_word.word} for cheap?",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
expect {
|
expect {
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result).to_not be_success
|
expect(result).to_not be_success
|
||||||
expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_words', words: [block_word.word, another_block_word.word].sort.join(', ')))
|
expect(result.errors[:base]&.first).to eq(
|
||||||
|
I18n.t(
|
||||||
|
"contains_blocked_words",
|
||||||
|
words: [block_word.word, another_block_word.word].sort.join(", "),
|
||||||
|
),
|
||||||
|
)
|
||||||
}.to_not change { Post.count }
|
}.to_not change { Post.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should block in a private message too" do
|
it "should block in a private message too" do
|
||||||
manager = NewPostManager.new(
|
manager =
|
||||||
tl2_user,
|
NewPostManager.new(
|
||||||
raw: "Want some #{block_word.word} for cheap?",
|
tl2_user,
|
||||||
title: 'this is a new title',
|
raw: "Want some #{block_word.word} for cheap?",
|
||||||
archetype: Archetype.private_message,
|
title: "this is a new title",
|
||||||
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username
|
archetype: Archetype.private_message,
|
||||||
)
|
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username,
|
||||||
|
)
|
||||||
should_block_post(manager)
|
should_block_post(manager)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "blocks on revisions" do
|
it "blocks on revisions" do
|
||||||
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
|
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
|
||||||
expect {
|
expect {
|
||||||
PostRevisor.new(post).revise!(post.user, { raw: "Want some #{block_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds)
|
PostRevisor.new(post).revise!(
|
||||||
|
post.user,
|
||||||
|
{ raw: "Want some #{block_word.word} for cheap?" },
|
||||||
|
revised_at: post.updated_at + 10.seconds,
|
||||||
|
)
|
||||||
expect(post.errors).to be_present
|
expect(post.errors).to be_present
|
||||||
post.reload
|
post.reload
|
||||||
}.to_not change { post.raw }
|
}.to_not change { post.raw }
|
||||||
|
@ -90,27 +131,48 @@ RSpec.describe WatchedWord do
|
||||||
|
|
||||||
context "with require_approval" do
|
context "with require_approval" do
|
||||||
it "should queue the post for approval" do
|
it "should queue the post for approval" do
|
||||||
manager = NewPostManager.new(tl2_user, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
tl2_user,
|
||||||
|
raw: "My dog's name is #{require_approval_word.word}.",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result.action).to eq(:enqueued)
|
expect(result.action).to eq(:enqueued)
|
||||||
expect(result.reason).to eq(:watched_word)
|
expect(result.reason).to eq(:watched_word)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "looks at title too" do
|
it "looks at title too" do
|
||||||
manager = NewPostManager.new(tl2_user, title: "You won't believe these #{require_approval_word.word} dog names!", raw: "My dog's name is Porkins.", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
tl2_user,
|
||||||
|
title: "You won't believe these #{require_approval_word.word} dog names!",
|
||||||
|
raw: "My dog's name is Porkins.",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result.action).to eq(:enqueued)
|
expect(result.action).to eq(:enqueued)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not queue posts from admin" do
|
it "should not queue posts from admin" do
|
||||||
manager = NewPostManager.new(admin, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
admin,
|
||||||
|
raw: "My dog's name is #{require_approval_word.word}.",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result).to be_success
|
expect(result).to be_success
|
||||||
expect(result.action).to eq(:create_post)
|
expect(result.action).to eq(:create_post)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not queue posts from moderator" do
|
it "should not queue posts from moderator" do
|
||||||
manager = NewPostManager.new(moderator, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id)
|
manager =
|
||||||
|
NewPostManager.new(
|
||||||
|
moderator,
|
||||||
|
raw: "My dog's name is #{require_approval_word.word}.",
|
||||||
|
topic_id: topic.id,
|
||||||
|
)
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result).to be_success
|
expect(result).to be_success
|
||||||
expect(result.action).to eq(:create_post)
|
expect(result.action).to eq(:create_post)
|
||||||
|
@ -118,13 +180,14 @@ RSpec.describe WatchedWord do
|
||||||
|
|
||||||
it "doesn't need approval in a private message" do
|
it "doesn't need approval in a private message" do
|
||||||
Group.refresh_automatic_groups!
|
Group.refresh_automatic_groups!
|
||||||
manager = NewPostManager.new(
|
manager =
|
||||||
tl2_user,
|
NewPostManager.new(
|
||||||
raw: "Want some #{require_approval_word.word} for cheap?",
|
tl2_user,
|
||||||
title: 'this is a new title',
|
raw: "Want some #{require_approval_word.word} for cheap?",
|
||||||
archetype: Archetype.private_message,
|
title: "this is a new title",
|
||||||
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username
|
archetype: Archetype.private_message,
|
||||||
)
|
target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username,
|
||||||
|
)
|
||||||
result = manager.perform
|
result = manager.perform
|
||||||
expect(result).to be_success
|
expect(result).to be_success
|
||||||
expect(result.action).to eq(:create_post)
|
expect(result.action).to eq(:create_post)
|
||||||
|
@ -134,74 +197,122 @@ RSpec.describe WatchedWord do
|
||||||
context "with flag" do
|
context "with flag" do
|
||||||
def should_flag_post(author, raw, topic)
|
def should_flag_post(author, raw, topic)
|
||||||
post = Fabricate(:post, raw: raw, topic: topic, user: author)
|
post = Fabricate(:post, raw: raw, topic: topic, user: author)
|
||||||
expect {
|
expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { PostAction.count }.by(
|
||||||
Jobs::ProcessPost.new.execute(post_id: post.id)
|
1,
|
||||||
}.to change { PostAction.count }.by(1)
|
)
|
||||||
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
|
expect(
|
||||||
|
PostAction.where(
|
||||||
|
post_id: post.id,
|
||||||
|
post_action_type_id: PostActionType.types[:inappropriate],
|
||||||
|
).exists?,
|
||||||
|
).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_not_flag_post(author, raw, topic)
|
def should_not_flag_post(author, raw, topic)
|
||||||
post = Fabricate(:post, raw: raw, topic: topic, user: author)
|
post = Fabricate(:post, raw: raw, topic: topic, user: author)
|
||||||
expect {
|
expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to_not change { PostAction.count }
|
||||||
Jobs::ProcessPost.new.execute(post_id: post.id)
|
|
||||||
}.to_not change { PostAction.count }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should flag the post as inappropriate" do
|
it "should flag the post as inappropriate" do
|
||||||
topic = Fabricate(:topic, user: tl2_user)
|
topic = Fabricate(:topic, user: tl2_user)
|
||||||
post = Fabricate(:post, raw: "I said.... #{flag_word.word}", topic: topic, user: tl2_user)
|
post = Fabricate(:post, raw: "I said.... #{flag_word.word}", topic: topic, user: tl2_user)
|
||||||
Jobs::ProcessPost.new.execute(post_id: post.id)
|
Jobs::ProcessPost.new.execute(post_id: post.id)
|
||||||
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
|
expect(
|
||||||
|
PostAction.where(
|
||||||
|
post_id: post.id,
|
||||||
|
post_action_type_id: PostActionType.types[:inappropriate],
|
||||||
|
).exists?,
|
||||||
|
).to eq(true)
|
||||||
reviewable = ReviewableFlaggedPost.where(target: post)
|
reviewable = ReviewableFlaggedPost.where(target: post)
|
||||||
expect(reviewable).to be_present
|
expect(reviewable).to be_present
|
||||||
expect(ReviewableScore.where(reviewable: reviewable, reason: 'watched_word')).to be_present
|
expect(ReviewableScore.where(reviewable: reviewable, reason: "watched_word")).to be_present
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should look at the title too" do
|
it "should look at the title too" do
|
||||||
should_flag_post(tl2_user, "I thought the movie was not bad actually.", Fabricate(:topic, user: tl2_user, title: "Read my #{flag_word.word} review!"))
|
should_flag_post(
|
||||||
|
tl2_user,
|
||||||
|
"I thought the movie was not bad actually.",
|
||||||
|
Fabricate(:topic, user: tl2_user, title: "Read my #{flag_word.word} review!"),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "shouldn't flag posts by admin" do
|
it "shouldn't flag posts by admin" do
|
||||||
should_not_flag_post(admin, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: admin))
|
should_not_flag_post(
|
||||||
|
admin,
|
||||||
|
"I thought the #{flag_word.word} was bad.",
|
||||||
|
Fabricate(:topic, user: admin),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "shouldn't flag posts by moderator" do
|
it "shouldn't flag posts by moderator" do
|
||||||
should_not_flag_post(moderator, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: moderator))
|
should_not_flag_post(
|
||||||
|
moderator,
|
||||||
|
"I thought the #{flag_word.word} was bad.",
|
||||||
|
Fabricate(:topic, user: moderator),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is compatible with flag_sockpuppets" do
|
it "is compatible with flag_sockpuppets" do
|
||||||
SiteSetting.flag_sockpuppets = true
|
SiteSetting.flag_sockpuppets = true
|
||||||
ip_address = '182.189.119.174'
|
ip_address = "182.189.119.174"
|
||||||
user1 = Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago)
|
user1 = Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago)
|
||||||
user2 = Fabricate(:user, ip_address: ip_address)
|
user2 = Fabricate(:user, ip_address: ip_address)
|
||||||
first = create_post(user: user1, created_at: 2.days.ago)
|
first = create_post(user: user1, created_at: 2.days.ago)
|
||||||
sockpuppet_post = create_post(user: user2, topic: first.topic, raw: "I thought the #{flag_word.word} was bad.")
|
sockpuppet_post =
|
||||||
|
create_post(
|
||||||
|
user: user2,
|
||||||
|
topic: first.topic,
|
||||||
|
raw: "I thought the #{flag_word.word} was bad.",
|
||||||
|
)
|
||||||
expect(PostAction.where(post_id: sockpuppet_post.id).count).to eq(1)
|
expect(PostAction.where(post_id: sockpuppet_post.id).count).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "flags in private message too" do
|
it "flags in private message too" do
|
||||||
post = Fabricate(:private_message_post, raw: "Want some #{flag_word.word} for cheap?", user: tl2_user)
|
post =
|
||||||
expect {
|
Fabricate(
|
||||||
Jobs::ProcessPost.new.execute(post_id: post.id)
|
:private_message_post,
|
||||||
}.to change { PostAction.count }.by(1)
|
raw: "Want some #{flag_word.word} for cheap?",
|
||||||
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
|
user: tl2_user,
|
||||||
|
)
|
||||||
|
expect { Jobs::ProcessPost.new.execute(post_id: post.id) }.to change { PostAction.count }.by(
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
PostAction.where(
|
||||||
|
post_id: post.id,
|
||||||
|
post_action_type_id: PostActionType.types[:inappropriate],
|
||||||
|
).exists?,
|
||||||
|
).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "flags on revisions" do
|
it "flags on revisions" do
|
||||||
Jobs.run_immediately!
|
Jobs.run_immediately!
|
||||||
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
|
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user)
|
||||||
expect {
|
expect {
|
||||||
PostRevisor.new(post).revise!(post.user, { raw: "Want some #{flag_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds)
|
PostRevisor.new(post).revise!(
|
||||||
|
post.user,
|
||||||
|
{ raw: "Want some #{flag_word.word} for cheap?" },
|
||||||
|
revised_at: post.updated_at + 10.seconds,
|
||||||
|
)
|
||||||
}.to change { PostAction.count }.by(1)
|
}.to change { PostAction.count }.by(1)
|
||||||
expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true)
|
expect(
|
||||||
|
PostAction.where(
|
||||||
|
post_id: post.id,
|
||||||
|
post_action_type_id: PostActionType.types[:inappropriate],
|
||||||
|
).exists?,
|
||||||
|
).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not flag on rebake" do
|
it "should not flag on rebake" do
|
||||||
post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user, raw: "I have coupon codes. Message me.")
|
post =
|
||||||
|
Fabricate(
|
||||||
|
:post,
|
||||||
|
topic: Fabricate(:topic, user: tl2_user),
|
||||||
|
user: tl2_user,
|
||||||
|
raw: "I have coupon codes. Message me.",
|
||||||
|
)
|
||||||
Fabricate(:watched_word, action: WatchedWord.actions[:flag], word: "coupon")
|
Fabricate(:watched_word, action: WatchedWord.actions[:flag], word: "coupon")
|
||||||
expect {
|
expect { post.rebake! }.to_not change { PostAction.count }
|
||||||
post.rebake!
|
|
||||||
}.to_not change { PostAction.count }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
def list_files(base_dir, pattern = '*')
|
def list_files(base_dir, pattern = "*")
|
||||||
Dir[File.join("#{base_dir}", pattern)]
|
Dir[File.join("#{base_dir}", pattern)]
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_js_files(base_dir)
|
def list_js_files(base_dir)
|
||||||
list_files(base_dir, '**/*.es6')
|
list_files(base_dir, "**/*.es6")
|
||||||
end
|
end
|
||||||
|
|
||||||
def grep_files(files, regex)
|
def grep_files(files, regex)
|
||||||
|
@ -17,10 +17,10 @@ def grep_file(file, regex)
|
||||||
lines.count > 0 ? file : nil
|
lines.count > 0 ? file : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
RSpec.describe 'Coding style' do
|
RSpec.describe "Coding style" do
|
||||||
describe 'Javascript' do
|
describe "Javascript" do
|
||||||
it 'prevents this.get("foo") pattern' do
|
it 'prevents this.get("foo") pattern' do
|
||||||
js_files = list_js_files('app/assets/javascripts')
|
js_files = list_js_files("app/assets/javascripts")
|
||||||
offenses = grep_files(js_files, /this\.get\("\w+"\)/)
|
offenses = grep_files(js_files, /this\.get\("\w+"\)/)
|
||||||
|
|
||||||
expect(offenses).to be_empty, <<~TEXT
|
expect(offenses).to be_empty, <<~TEXT
|
||||||
|
@ -33,7 +33,7 @@ RSpec.describe 'Coding style' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Post Migrations' do
|
describe "Post Migrations" do
|
||||||
def check_offenses(files, method_name, constant_name)
|
def check_offenses(files, method_name, constant_name)
|
||||||
method_name_regex = /#{Regexp.escape(method_name)}/
|
method_name_regex = /#{Regexp.escape(method_name)}/
|
||||||
constant_name_regex = /#{Regexp.escape(constant_name)}/
|
constant_name_regex = /#{Regexp.escape(constant_name)}/
|
||||||
|
@ -57,8 +57,8 @@ RSpec.describe 'Coding style' do
|
||||||
contains_method_name ? contains_constant_name : true
|
contains_method_name ? contains_constant_name : true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ensures dropped tables and columns are stored in constants' do
|
it "ensures dropped tables and columns are stored in constants" do
|
||||||
migration_files = list_files('db/post_migrate', '**/*.rb')
|
migration_files = list_files("db/post_migrate", "**/*.rb")
|
||||||
|
|
||||||
check_offenses(migration_files, "ColumnDropper.execute_drop", "DROPPED_COLUMNS")
|
check_offenses(migration_files, "ColumnDropper.execute_drop", "DROPPED_COLUMNS")
|
||||||
check_offenses(migration_files, "TableDropper.execute_drop", "DROPPED_TABLES")
|
check_offenses(migration_files, "TableDropper.execute_drop", "DROPPED_TABLES")
|
||||||
|
|
|
@ -1,76 +1,72 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
RSpec.describe "CommonMark" do
|
RSpec.describe "CommonMark" do
|
||||||
it 'passes spec' do
|
it "passes spec" do
|
||||||
|
|
||||||
SiteSetting.traditional_markdown_linebreaks = true
|
SiteSetting.traditional_markdown_linebreaks = true
|
||||||
SiteSetting.enable_markdown_typographer = false
|
SiteSetting.enable_markdown_typographer = false
|
||||||
SiteSetting.highlighted_languages = 'ruby|aa'
|
SiteSetting.highlighted_languages = "ruby|aa"
|
||||||
|
|
||||||
html, state, md = nil
|
html, state, md = nil
|
||||||
failed = 0
|
failed = 0
|
||||||
|
|
||||||
File.readlines(Rails.root + 'spec/fixtures/md/spec.txt').each do |line|
|
File
|
||||||
if line == "```````````````````````````````` example\n"
|
.readlines(Rails.root + "spec/fixtures/md/spec.txt")
|
||||||
state = :example
|
.each do |line|
|
||||||
next
|
if line == "```````````````````````````````` example\n"
|
||||||
end
|
state = :example
|
||||||
|
next
|
||||||
if line == "````````````````````````````````\n"
|
|
||||||
md.gsub!('→', "\t")
|
|
||||||
html ||= String.new
|
|
||||||
html.gsub!('→', "\t")
|
|
||||||
html.strip!
|
|
||||||
|
|
||||||
# normalize brs
|
|
||||||
html.gsub!('<br />', '<br>')
|
|
||||||
html.gsub!('<hr />', '<hr>')
|
|
||||||
html.gsub!(/<img([^>]+) \/>/, "<img\\1>")
|
|
||||||
|
|
||||||
SiteSetting.enable_markdown_linkify = false
|
|
||||||
cooked = PrettyText.markdown(md, sanitize: false)
|
|
||||||
cooked.strip!
|
|
||||||
cooked.gsub!(" class=\"lang-auto\"", '')
|
|
||||||
cooked.gsub!(/<span class="hashtag">(.*)<\/span>/, "\\1")
|
|
||||||
cooked.gsub!(/<a name="(.*)" class="anchor" href="#\1*"><\/a>/, "")
|
|
||||||
# we support data-attributes which is not in the spec
|
|
||||||
cooked.gsub!("<pre data-code-startline=\"3\">", '<pre>')
|
|
||||||
# we don't care about this
|
|
||||||
cooked.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
|
||||||
html.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
|
||||||
html.gsub!("language-ruby", "lang-ruby")
|
|
||||||
html.gsub!("language-aa", "lang-aa")
|
|
||||||
# strip out unsupported languages
|
|
||||||
html.gsub!(/ class="language-[;f].*"/, "")
|
|
||||||
|
|
||||||
unless cooked == html
|
|
||||||
failed += 1
|
|
||||||
puts "FAILED SPEC"
|
|
||||||
puts "Expected: "
|
|
||||||
puts html
|
|
||||||
puts "Got: "
|
|
||||||
puts cooked
|
|
||||||
puts "Markdown: "
|
|
||||||
puts md
|
|
||||||
puts
|
|
||||||
end
|
end
|
||||||
html, state, md = nil
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
if state == :example && line == ".\n"
|
if line == "````````````````````````````````\n"
|
||||||
state = :html
|
md.gsub!("→", "\t")
|
||||||
next
|
html ||= String.new
|
||||||
end
|
html.gsub!("→", "\t")
|
||||||
|
html.strip!
|
||||||
|
|
||||||
if state == :example
|
# normalize brs
|
||||||
md = (md || String.new) << line
|
html.gsub!("<br />", "<br>")
|
||||||
end
|
html.gsub!("<hr />", "<hr>")
|
||||||
|
html.gsub!(%r{<img([^>]+) />}, "<img\\1>")
|
||||||
|
|
||||||
if state == :html
|
SiteSetting.enable_markdown_linkify = false
|
||||||
html = (html || String.new) << line
|
cooked = PrettyText.markdown(md, sanitize: false)
|
||||||
end
|
cooked.strip!
|
||||||
|
cooked.gsub!(" class=\"lang-auto\"", "")
|
||||||
|
cooked.gsub!(%r{<span class="hashtag">(.*)</span>}, "\\1")
|
||||||
|
cooked.gsub!(%r{<a name="(.*)" class="anchor" href="#\1*"></a>}, "")
|
||||||
|
# we support data-attributes which is not in the spec
|
||||||
|
cooked.gsub!("<pre data-code-startline=\"3\">", "<pre>")
|
||||||
|
# we don't care about this
|
||||||
|
cooked.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
||||||
|
html.gsub!("<blockquote>\n</blockquote>", "<blockquote></blockquote>")
|
||||||
|
html.gsub!("language-ruby", "lang-ruby")
|
||||||
|
html.gsub!("language-aa", "lang-aa")
|
||||||
|
# strip out unsupported languages
|
||||||
|
html.gsub!(%r{ class="language-[;f].*"}, "")
|
||||||
|
|
||||||
end
|
unless cooked == html
|
||||||
|
failed += 1
|
||||||
|
puts "FAILED SPEC"
|
||||||
|
puts "Expected: "
|
||||||
|
puts html
|
||||||
|
puts "Got: "
|
||||||
|
puts cooked
|
||||||
|
puts "Markdown: "
|
||||||
|
puts md
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
html, state, md = nil
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == :example && line == ".\n"
|
||||||
|
state = :html
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
md = (md || String.new) << line if state == :example
|
||||||
|
|
||||||
|
html = (html || String.new) << line if state == :html
|
||||||
|
end
|
||||||
|
|
||||||
expect(failed).to eq(0)
|
expect(failed).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,10 +37,12 @@ RSpec.describe "i18n integrity checks" do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "has an i18n key for each Badge description" do
|
it "has an i18n key for each Badge description" do
|
||||||
Badge.where(system: true).each do |b|
|
Badge
|
||||||
expect(b.long_description).to be_present
|
.where(system: true)
|
||||||
expect(b.description).to be_present
|
.each do |b|
|
||||||
end
|
expect(b.long_description).to be_present
|
||||||
|
expect(b.description).to be_present
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Dir["#{Rails.root}/config/locales/{client,server}.*.yml"].each do |path|
|
Dir["#{Rails.root}/config/locales/{client,server}.*.yml"].each do |path|
|
||||||
|
@ -116,20 +118,14 @@ RSpec.describe "fallbacks" do
|
||||||
it "finds the fallback translation" do
|
it "finds the fallback translation" do
|
||||||
I18n.backend.store_translations(:en, test: "en test")
|
I18n.backend.store_translations(:en, test: "en test")
|
||||||
|
|
||||||
I18n.with_locale("pl_PL") do
|
I18n.with_locale("pl_PL") { expect(I18n.t("test")).to eq("en test") }
|
||||||
expect(I18n.t("test")).to eq("en test")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when in a multi-threaded environment" do
|
context "when in a multi-threaded environment" do
|
||||||
it "finds the fallback translation" do
|
it "finds the fallback translation" do
|
||||||
I18n.backend.store_translations(:en, test: "en test")
|
I18n.backend.store_translations(:en, test: "en test")
|
||||||
|
|
||||||
thread = Thread.new do
|
thread = Thread.new { I18n.with_locale("pl_PL") { expect(I18n.t("test")).to eq("en test") } }
|
||||||
I18n.with_locale("pl_PL") do
|
|
||||||
expect(I18n.t("test")).to eq("en test")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
thread.join
|
thread.join
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe "constants match ruby" do
|
RSpec.describe "constants match ruby" do
|
||||||
|
|
||||||
let(:ctx) { MiniRacer::Context.new }
|
let(:ctx) { MiniRacer::Context.new }
|
||||||
|
|
||||||
def parse(file)
|
def parse(file)
|
||||||
# mini racer doesn't handle JS modules so we'll do this hack
|
# mini racer doesn't handle JS modules so we'll do this hack
|
||||||
source = File.read("#{Rails.root}/app/assets/javascripts/#{file}")
|
source = File.read("#{Rails.root}/app/assets/javascripts/#{file}")
|
||||||
source.gsub!(/^export */, '')
|
source.gsub!(/^export */, "")
|
||||||
ctx.eval(source)
|
ctx.eval(source)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -16,12 +15,9 @@ RSpec.describe "constants match ruby" do
|
||||||
parse("pretty-text/addon/emoji/version.js")
|
parse("pretty-text/addon/emoji/version.js")
|
||||||
|
|
||||||
priorities = ctx.eval("SEARCH_PRIORITIES")
|
priorities = ctx.eval("SEARCH_PRIORITIES")
|
||||||
Searchable::PRIORITIES.each do |key, value|
|
Searchable::PRIORITIES.each { |key, value| expect(priorities[key.to_s]).to eq(value) }
|
||||||
expect(priorities[key.to_s]).to eq(value)
|
|
||||||
end
|
|
||||||
|
|
||||||
expect(ctx.eval("SEARCH_PHRASE_REGEXP")).to eq(Search::PHRASE_MATCH_REGEXP_PATTERN)
|
expect(ctx.eval("SEARCH_PHRASE_REGEXP")).to eq(Search::PHRASE_MATCH_REGEXP_PATTERN)
|
||||||
expect(ctx.eval("IMAGE_VERSION")).to eq(Emoji::EMOJI_VERSION)
|
expect(ctx.eval("IMAGE_VERSION")).to eq(Emoji::EMOJI_VERSION)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe 'Oj' do
|
RSpec.describe "Oj" do
|
||||||
it "is enabled" do
|
it "is enabled" do
|
||||||
classes = Set.new
|
classes = Set.new
|
||||||
tracer = TracePoint.new(:c_call) { |tp| classes << tp.defined_class }
|
tracer = TracePoint.new(:c_call) { |tp| classes << tp.defined_class }
|
||||||
|
@ -11,7 +11,7 @@ RSpec.describe 'Oj' do
|
||||||
|
|
||||||
it "escapes HTML entities the same as ActiveSupport" do
|
it "escapes HTML entities the same as ActiveSupport" do
|
||||||
expect("<b>hello</b>".to_json).to eq("\"\\u003cb\\u003ehello\\u003c/b\\u003e\"")
|
expect("<b>hello</b>".to_json).to eq("\"\\u003cb\\u003ehello\\u003c/b\\u003e\"")
|
||||||
expect('"hello world"'.to_json). to eq('"\"hello world\""')
|
expect('"hello world"'.to_json).to eq('"\"hello world\""')
|
||||||
expect("\u2028\u2029><&".to_json).to eq('"\u2028\u2029\u003e\u003c\u0026"')
|
expect("\u2028\u2029><&".to_json).to eq('"\u2028\u2029\u003e\u003c\u0026"')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
RSpec.describe ::Jobs::Onceoff do
|
RSpec.describe ::Jobs::Onceoff do
|
||||||
it "can run all once off jobs without errors" do
|
it "can run all once off jobs without errors" do
|
||||||
# Load all once offs
|
# Load all once offs
|
||||||
Dir[Rails.root + 'app/jobs/onceoff/*.rb'].each do |f|
|
Dir[Rails.root + "app/jobs/onceoff/*.rb"].each do |f|
|
||||||
require_relative '../../app/jobs/onceoff/' + File.basename(f)
|
require_relative "../../app/jobs/onceoff/" + File.basename(f)
|
||||||
end
|
end
|
||||||
|
|
||||||
ObjectSpace.each_object(Class)
|
ObjectSpace
|
||||||
|
.each_object(Class)
|
||||||
.select { |klass| klass.superclass == ::Jobs::Onceoff }
|
.select { |klass| klass.superclass == ::Jobs::Onceoff }
|
||||||
.each { |job| job.new.execute_onceoff(nil) }
|
.each { |job| job.new.execute_onceoff(nil) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe "site setting integrity checks" do
|
RSpec.describe "site setting integrity checks" do
|
||||||
let(:site_setting_file) { File.join(Rails.root, 'config', 'site_settings.yml') }
|
let(:site_setting_file) { File.join(Rails.root, "config", "site_settings.yml") }
|
||||||
let(:yaml) { YAML.load_file(site_setting_file) }
|
let(:yaml) { YAML.load_file(site_setting_file) }
|
||||||
|
|
||||||
%w(hidden client).each do |property|
|
%w[hidden client].each do |property|
|
||||||
it "set #{property} value as true or not set" do
|
it "set #{property} value as true or not set" do
|
||||||
yaml.each_value do |category|
|
yaml.each_value do |category|
|
||||||
category.each_value do |setting|
|
category.each_value do |setting|
|
||||||
|
@ -24,16 +24,16 @@ RSpec.describe "site setting integrity checks" do
|
||||||
yaml.each_value do |category|
|
yaml.each_value do |category|
|
||||||
category.each do |setting_name, setting|
|
category.each do |setting_name, setting|
|
||||||
next unless setting.is_a?(Hash)
|
next unless setting.is_a?(Hash)
|
||||||
if setting['locale_default']
|
if setting["locale_default"]
|
||||||
setting['locale_default'].each_pair do |k, v|
|
setting["locale_default"].each_pair do |k, v|
|
||||||
expect(LocaleSiteSetting.valid_value?(k.to_s)).to be_truthy,
|
expect(LocaleSiteSetting.valid_value?(k.to_s)).to be_truthy,
|
||||||
"'#{k}' is not a valid locale_default key for '#{setting_name}' site setting"
|
"'#{k}' is not a valid locale_default key for '#{setting_name}' site setting"
|
||||||
|
|
||||||
case setting['default']
|
case setting["default"]
|
||||||
when TrueClass, FalseClass
|
when TrueClass, FalseClass
|
||||||
expect(v.class == TrueClass || v.class == FalseClass).to be_truthy
|
expect(v.class == TrueClass || v.class == FalseClass).to be_truthy
|
||||||
else
|
else
|
||||||
expect(v).to be_a_kind_of(setting['default'].class)
|
expect(v).to be_a_kind_of(setting["default"].class)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Jobs::AboutStats do
|
RSpec.describe Jobs::AboutStats do
|
||||||
it 'caches the stats' do
|
it "caches the stats" do
|
||||||
begin
|
begin
|
||||||
stats = About.fetch_stats.to_json
|
stats = About.fetch_stats.to_json
|
||||||
cache_key = About.stats_cache_key
|
cache_key = About.stats_cache_key
|
||||||
|
|
|
@ -6,33 +6,33 @@ RSpec.describe Jobs::ActivationReminderEmails do
|
||||||
# should be between 2 and 3 days
|
# should be between 2 and 3 days
|
||||||
let(:created_at) { 50.hours.ago }
|
let(:created_at) { 50.hours.ago }
|
||||||
|
|
||||||
it 'should email inactive users' do
|
it "should email inactive users" do
|
||||||
user = Fabricate(:user, active: false, created_at: created_at)
|
user = Fabricate(:user, active: false, created_at: created_at)
|
||||||
|
|
||||||
expect { described_class.new.execute({}) }
|
expect { described_class.new.execute({}) }.to change { ActionMailer::Base.deliveries.size }.by(
|
||||||
.to change { ActionMailer::Base.deliveries.size }.by(1)
|
1,
|
||||||
.and change { user.email_tokens.count }.by(1)
|
).and change { user.email_tokens.count }.by(1)
|
||||||
|
|
||||||
expect(user.custom_fields['activation_reminder']).to eq("t")
|
expect(user.custom_fields["activation_reminder"]).to eq("t")
|
||||||
expect { described_class.new.execute({}) }.not_to change { ActionMailer::Base.deliveries.size }
|
expect { described_class.new.execute({}) }.not_to change { ActionMailer::Base.deliveries.size }
|
||||||
|
|
||||||
user.activate
|
user.activate
|
||||||
expect(user.reload.custom_fields['activation_reminder']).to eq(nil)
|
expect(user.reload.custom_fields["activation_reminder"]).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not email active users' do
|
it "should not email active users" do
|
||||||
user = Fabricate(:user, active: true, created_at: created_at)
|
user = Fabricate(:user, active: true, created_at: created_at)
|
||||||
|
|
||||||
expect { described_class.new.execute({}) }
|
expect { described_class.new.execute({}) }.to not_change {
|
||||||
.to not_change { ActionMailer::Base.deliveries.size }
|
ActionMailer::Base.deliveries.size
|
||||||
.and not_change { user.email_tokens.count }
|
}.and not_change { user.email_tokens.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not email staged users' do
|
it "should not email staged users" do
|
||||||
user = Fabricate(:user, active: false, staged: true, created_at: created_at)
|
user = Fabricate(:user, active: false, staged: true, created_at: created_at)
|
||||||
|
|
||||||
expect { described_class.new.execute({}) }
|
expect { described_class.new.execute({}) }.to not_change {
|
||||||
.to not_change { ActionMailer::Base.deliveries.size }
|
ActionMailer::Base.deliveries.size
|
||||||
.and not_change { user.email_tokens.count }
|
}.and not_change { user.email_tokens.count }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,12 +4,10 @@ RSpec.describe Jobs::AutoExpireUserApiKeys do
|
||||||
fab!(:key1) { Fabricate(:readonly_user_api_key) }
|
fab!(:key1) { Fabricate(:readonly_user_api_key) }
|
||||||
fab!(:key2) { Fabricate(:readonly_user_api_key) }
|
fab!(:key2) { Fabricate(:readonly_user_api_key) }
|
||||||
|
|
||||||
context 'when user api key is unused in last 1 days' do
|
context "when user api key is unused in last 1 days" do
|
||||||
before do
|
before { SiteSetting.expire_user_api_keys_days = 1 }
|
||||||
SiteSetting.expire_user_api_keys_days = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should revoke the key' do
|
it "should revoke the key" do
|
||||||
freeze_time
|
freeze_time
|
||||||
|
|
||||||
key1.update!(last_used_at: 2.days.ago)
|
key1.update!(last_used_at: 2.days.ago)
|
||||||
|
|
|
@ -9,15 +9,15 @@ RSpec.describe Jobs::AutoQueueHandler do
|
||||||
Fabricate(:user),
|
Fabricate(:user),
|
||||||
Fabricate(:post),
|
Fabricate(:post),
|
||||||
PostActionType.types[:spam],
|
PostActionType.types[:spam],
|
||||||
message: 'this is the initial message'
|
message: "this is the initial message",
|
||||||
).perform
|
).perform
|
||||||
end
|
end
|
||||||
|
|
||||||
fab!(:post_action) { spam_result.post_action }
|
fab!(:post_action) { spam_result.post_action }
|
||||||
fab!(:old) {
|
fab!(:old) do
|
||||||
spam_result.reviewable.update_column(:created_at, 61.days.ago)
|
spam_result.reviewable.update_column(:created_at, 61.days.ago)
|
||||||
spam_result.reviewable
|
spam_result.reviewable
|
||||||
}
|
end
|
||||||
|
|
||||||
fab!(:not_old) { Fabricate(:reviewable_flagged_post, created_at: 59.days.ago) }
|
fab!(:not_old) { Fabricate(:reviewable_flagged_post, created_at: 59.days.ago) }
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Jobs::AutomaticGroupMembership do
|
RSpec.describe Jobs::AutomaticGroupMembership do
|
||||||
|
|
||||||
it "raises an error when the group id is missing" do
|
it "raises an error when the group id is missing" do
|
||||||
expect { Jobs::AutomaticGroupMembership.new.execute({}) }.to raise_error(Discourse::InvalidParameters)
|
expect { Jobs::AutomaticGroupMembership.new.execute({}) }.to raise_error(
|
||||||
|
Discourse::InvalidParameters,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates the membership" do
|
it "updates the membership" do
|
||||||
|
@ -14,19 +15,28 @@ RSpec.describe Jobs::AutomaticGroupMembership do
|
||||||
user4 = Fabricate(:user, email: "yes@wat.com")
|
user4 = Fabricate(:user, email: "yes@wat.com")
|
||||||
EmailToken.confirm(Fabricate(:email_token, user: user4).token)
|
EmailToken.confirm(Fabricate(:email_token, user: user4).token)
|
||||||
user5 = Fabricate(:user, email: "sso@wat.com")
|
user5 = Fabricate(:user, email: "sso@wat.com")
|
||||||
user5.create_single_sign_on_record(external_id: 123, external_email: "hacker@wat.com", last_payload: "")
|
user5.create_single_sign_on_record(
|
||||||
|
external_id: 123,
|
||||||
|
external_email: "hacker@wat.com",
|
||||||
|
last_payload: "",
|
||||||
|
)
|
||||||
user6 = Fabricate(:user, email: "sso2@wat.com")
|
user6 = Fabricate(:user, email: "sso2@wat.com")
|
||||||
user6.create_single_sign_on_record(external_id: 456, external_email: "sso2@wat.com", last_payload: "")
|
user6.create_single_sign_on_record(
|
||||||
|
external_id: 456,
|
||||||
|
external_email: "sso2@wat.com",
|
||||||
|
last_payload: "",
|
||||||
|
)
|
||||||
|
|
||||||
group = Fabricate(:group, automatic_membership_email_domains: "wat.com")
|
group = Fabricate(:group, automatic_membership_email_domains: "wat.com")
|
||||||
|
|
||||||
automatic = nil
|
automatic = nil
|
||||||
called = false
|
called = false
|
||||||
|
|
||||||
blk = Proc.new do |_u, _g, options|
|
blk =
|
||||||
automatic = options[:automatic]
|
Proc.new do |_u, _g, options|
|
||||||
called = true
|
automatic = options[:automatic]
|
||||||
end
|
called = true
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
DiscourseEvent.on(:user_added_to_group, &blk)
|
DiscourseEvent.on(:user_added_to_group, &blk)
|
||||||
|
@ -47,5 +57,4 @@ RSpec.describe Jobs::AutomaticGroupMembership do
|
||||||
expect(group.users.include?(user6)).to eq(true)
|
expect(group.users.include?(user6)).to eq(true)
|
||||||
expect(group.user_count).to eq(2)
|
expect(group.user_count).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,13 +8,7 @@ RSpec.describe Jobs::BookmarkReminderNotifications do
|
||||||
let(:bookmark1) { Fabricate(:bookmark, user: user) }
|
let(:bookmark1) { Fabricate(:bookmark, user: user) }
|
||||||
let(:bookmark2) { Fabricate(:bookmark, user: user) }
|
let(:bookmark2) { Fabricate(:bookmark, user: user) }
|
||||||
let(:bookmark3) { Fabricate(:bookmark, user: user) }
|
let(:bookmark3) { Fabricate(:bookmark, user: user) }
|
||||||
let!(:bookmarks) do
|
let!(:bookmarks) { [bookmark1, bookmark2, bookmark3] }
|
||||||
[
|
|
||||||
bookmark1,
|
|
||||||
bookmark2,
|
|
||||||
bookmark3
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
# this is done to avoid model validations on Bookmark
|
# this is done to avoid model validations on Bookmark
|
||||||
|
@ -66,10 +60,12 @@ RSpec.describe Jobs::BookmarkReminderNotifications do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'will not send notification when topic is not available' do
|
it "will not send notification when topic is not available" do
|
||||||
bookmark1.bookmarkable.topic.destroy
|
bookmark1.bookmarkable.topic.destroy
|
||||||
bookmark2.bookmarkable.topic.destroy
|
bookmark2.bookmarkable.topic.destroy
|
||||||
bookmark3.bookmarkable.topic.destroy
|
bookmark3.bookmarkable.topic.destroy
|
||||||
expect { subject.execute }.not_to change { Notification.where(notification_type: Notification.types[:bookmark_reminder]).count }
|
expect { subject.execute }.not_to change {
|
||||||
|
Notification.where(notification_type: Notification.types[:bookmark_reminder]).count
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Jobs::BulkGrantTrustLevel do
|
RSpec.describe Jobs::BulkGrantTrustLevel do
|
||||||
|
|
||||||
it "raises an error when trust_level is missing" do
|
it "raises an error when trust_level is missing" do
|
||||||
expect { Jobs::BulkGrantTrustLevel.new.execute(user_ids: [1, 2]) }.to raise_error(Discourse::InvalidParameters)
|
expect { Jobs::BulkGrantTrustLevel.new.execute(user_ids: [1, 2]) }.to raise_error(
|
||||||
|
Discourse::InvalidParameters,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises an error when user_ids are missing" do
|
it "raises an error when user_ids are missing" do
|
||||||
expect { Jobs::BulkGrantTrustLevel.new.execute(trust_level: 0) }.to raise_error(Discourse::InvalidParameters)
|
expect { Jobs::BulkGrantTrustLevel.new.execute(trust_level: 0) }.to raise_error(
|
||||||
|
Discourse::InvalidParameters,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates the trust_level" do
|
it "updates the trust_level" do
|
||||||
|
|
|
@ -1,34 +1,43 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Jobs::BulkInvite do
|
RSpec.describe Jobs::BulkInvite do
|
||||||
describe '#execute' do
|
describe "#execute" do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
fab!(:admin) { Fabricate(:admin) }
|
fab!(:admin) { Fabricate(:admin) }
|
||||||
fab!(:group1) { Fabricate(:group, name: 'group1') }
|
fab!(:group1) { Fabricate(:group, name: "group1") }
|
||||||
fab!(:group2) { Fabricate(:group, name: 'group2') }
|
fab!(:group2) { Fabricate(:group, name: "group2") }
|
||||||
fab!(:topic) { Fabricate(:topic) }
|
fab!(:topic) { Fabricate(:topic) }
|
||||||
let(:staged_user) { Fabricate(:user, staged: true, active: false) }
|
let(:staged_user) { Fabricate(:user, staged: true, active: false) }
|
||||||
let(:email) { 'test@discourse.org' }
|
let(:email) { "test@discourse.org" }
|
||||||
let(:invites) { [{ email: user.email }, { email: staged_user.email }, { email: 'test2@discourse.org' }, { email: 'test@discourse.org', groups: 'GROUP1;group2', topic_id: topic.id }, { email: 'invalid' }] }
|
let(:invites) do
|
||||||
|
[
|
||||||
it 'raises an error when the invites array is missing' do
|
{ email: user.email },
|
||||||
expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) }
|
{ email: staged_user.email },
|
||||||
.to raise_error(Discourse::InvalidParameters, /invites/)
|
{ email: "test2@discourse.org" },
|
||||||
|
{ email: "test@discourse.org", groups: "GROUP1;group2", topic_id: topic.id },
|
||||||
|
{ email: "invalid" },
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an error when current_user_id is not valid' do
|
it "raises an error when the invites array is missing" do
|
||||||
expect { Jobs::BulkInvite.new.execute(invites: invites) }
|
expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) }.to raise_error(
|
||||||
.to raise_error(Discourse::InvalidParameters, /current_user_id/)
|
Discourse::InvalidParameters,
|
||||||
end
|
/invites/,
|
||||||
|
|
||||||
it 'creates the right invites' do
|
|
||||||
described_class.new.execute(
|
|
||||||
current_user_id: admin.id,
|
|
||||||
invites: invites
|
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when current_user_id is not valid" do
|
||||||
|
expect { Jobs::BulkInvite.new.execute(invites: invites) }.to raise_error(
|
||||||
|
Discourse::InvalidParameters,
|
||||||
|
/current_user_id/,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates the right invites" do
|
||||||
|
described_class.new.execute(current_user_id: admin.id, invites: invites)
|
||||||
|
|
||||||
expect(Invite.exists?(email: staged_user.email)).to eq(true)
|
expect(Invite.exists?(email: staged_user.email)).to eq(true)
|
||||||
expect(Invite.exists?(email: 'test2@discourse.org')).to eq(true)
|
expect(Invite.exists?(email: "test2@discourse.org")).to eq(true)
|
||||||
|
|
||||||
invite = Invite.last
|
invite = Invite.last
|
||||||
expect(invite.email).to eq(email)
|
expect(invite.email).to eq(email)
|
||||||
|
@ -42,13 +51,10 @@ RSpec.describe Jobs::BulkInvite do
|
||||||
expect(post.raw).to include("1 error")
|
expect(post.raw).to include("1 error")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create invited groups for automatic groups' do
|
it "does not create invited groups for automatic groups" do
|
||||||
group2.update!(automatic: true)
|
group2.update!(automatic: true)
|
||||||
|
|
||||||
described_class.new.execute(
|
described_class.new.execute(current_user_id: admin.id, invites: invites)
|
||||||
current_user_id: admin.id,
|
|
||||||
invites: invites
|
|
||||||
)
|
|
||||||
|
|
||||||
invite = Invite.last
|
invite = Invite.last
|
||||||
expect(invite.email).to eq(email)
|
expect(invite.email).to eq(email)
|
||||||
|
@ -58,37 +64,31 @@ RSpec.describe Jobs::BulkInvite do
|
||||||
expect(post.raw).to include("1 warning")
|
expect(post.raw).to include("1 warning")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create invited groups record if the user can not manage the group' do
|
it "does not create invited groups record if the user can not manage the group" do
|
||||||
group1.add_owner(user)
|
group1.add_owner(user)
|
||||||
|
|
||||||
described_class.new.execute(
|
described_class.new.execute(current_user_id: user.id, invites: invites)
|
||||||
current_user_id: user.id,
|
|
||||||
invites: invites
|
|
||||||
)
|
|
||||||
|
|
||||||
invite = Invite.last
|
invite = Invite.last
|
||||||
expect(invite.email).to eq(email)
|
expect(invite.email).to eq(email)
|
||||||
expect(invite.invited_groups.pluck(:group_id)).to contain_exactly(group1.id)
|
expect(invite.invited_groups.pluck(:group_id)).to contain_exactly(group1.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'adds existing users to valid groups' do
|
it "adds existing users to valid groups" do
|
||||||
existing_user = Fabricate(:user, email: 'test@discourse.org')
|
existing_user = Fabricate(:user, email: "test@discourse.org")
|
||||||
|
|
||||||
group2.update!(automatic: true)
|
group2.update!(automatic: true)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
described_class.new.execute(
|
described_class.new.execute(current_user_id: admin.id, invites: invites)
|
||||||
current_user_id: admin.id,
|
|
||||||
invites: invites
|
|
||||||
)
|
|
||||||
end.to change { Invite.count }.by(2)
|
end.to change { Invite.count }.by(2)
|
||||||
|
|
||||||
expect(Invite.exists?(email: staged_user.email)).to eq(true)
|
expect(Invite.exists?(email: staged_user.email)).to eq(true)
|
||||||
expect(Invite.exists?(email: 'test2@discourse.org')).to eq(true)
|
expect(Invite.exists?(email: "test2@discourse.org")).to eq(true)
|
||||||
expect(existing_user.reload.groups).to eq([group1])
|
expect(existing_user.reload.groups).to eq([group1])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can create staged users and prepopulate user fields' do
|
it "can create staged users and prepopulate user fields" do
|
||||||
user_field = Fabricate(:user_field, name: "Location")
|
user_field = Fabricate(:user_field, name: "Location")
|
||||||
user_field_color = Fabricate(:user_field, field_type: "dropdown", name: "Color")
|
user_field_color = Fabricate(:user_field, field_type: "dropdown", name: "Color")
|
||||||
user_field_color.user_field_options.create!(value: "Red")
|
user_field_color.user_field_options.create!(value: "Red")
|
||||||
|
@ -98,40 +98,33 @@ RSpec.describe Jobs::BulkInvite do
|
||||||
described_class.new.execute(
|
described_class.new.execute(
|
||||||
current_user_id: admin.id,
|
current_user_id: admin.id,
|
||||||
invites: [
|
invites: [
|
||||||
{ email: 'test@discourse.org' }, # new user without user fields
|
{ email: "test@discourse.org" }, # new user without user fields
|
||||||
{ email: user.email, location: 'value 1', color: 'blue' }, # existing user with user fields
|
{ email: user.email, location: "value 1", color: "blue" }, # existing user with user fields
|
||||||
{ email: staged_user.email, location: 'value 2', color: 'redd' }, # existing staged user with user fields
|
{ email: staged_user.email, location: "value 2", color: "redd" }, # existing staged user with user fields
|
||||||
{ email: 'test2@discourse.org', location: 'value 3' } # new staged user with user fields
|
{ email: "test2@discourse.org", location: "value 3" }, # new staged user with user fields
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(Invite.count).to eq(3)
|
expect(Invite.count).to eq(3)
|
||||||
expect(User.where(staged: true).find_by_email('test@discourse.org')).to eq(nil)
|
expect(User.where(staged: true).find_by_email("test@discourse.org")).to eq(nil)
|
||||||
expect(user.user_fields[user_field.id.to_s]).to eq('value 1')
|
expect(user.user_fields[user_field.id.to_s]).to eq("value 1")
|
||||||
expect(user.user_fields[user_field_color.id.to_s]).to eq('Blue')
|
expect(user.user_fields[user_field_color.id.to_s]).to eq("Blue")
|
||||||
expect(staged_user.user_fields[user_field.id.to_s]).to eq('value 2')
|
expect(staged_user.user_fields[user_field.id.to_s]).to eq("value 2")
|
||||||
expect(staged_user.user_fields[user_field_color.id.to_s]).to eq(nil)
|
expect(staged_user.user_fields[user_field_color.id.to_s]).to eq(nil)
|
||||||
new_staged_user = User.where(staged: true).find_by_email('test2@discourse.org')
|
new_staged_user = User.where(staged: true).find_by_email("test2@discourse.org")
|
||||||
expect(new_staged_user.user_fields[user_field.id.to_s]).to eq('value 3')
|
expect(new_staged_user.user_fields[user_field.id.to_s]).to eq("value 3")
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are more than 200 invites' do
|
context "when there are more than 200 invites" do
|
||||||
let(:bulk_invites) { [] }
|
let(:bulk_invites) { [] }
|
||||||
|
|
||||||
before do
|
before { 202.times { |i| bulk_invites << { email: "test_#{i}@discourse.org" } } }
|
||||||
202.times do |i|
|
|
||||||
bulk_invites << { "email": "test_#{i}@discourse.org" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'rate limits email sending' do
|
it "rate limits email sending" do
|
||||||
described_class.new.execute(
|
described_class.new.execute(current_user_id: admin.id, invites: bulk_invites)
|
||||||
current_user_id: admin.id,
|
|
||||||
invites: bulk_invites
|
|
||||||
)
|
|
||||||
|
|
||||||
invite = Invite.last
|
invite = Invite.last
|
||||||
expect(invite.email).to eq('test_201@discourse.org')
|
expect(invite.email).to eq("test_201@discourse.org")
|
||||||
expect(invite.emailed_status).to eq(Invite.emailed_status_types[:bulk_pending])
|
expect(invite.emailed_status).to eq(Invite.emailed_status_types[:bulk_pending])
|
||||||
expect(Jobs::ProcessBulkInviteEmails.jobs.size).to eq(1)
|
expect(Jobs::ProcessBulkInviteEmails.jobs.size).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,5 +31,4 @@ RSpec.describe Jobs::BumpTopic do
|
||||||
|
|
||||||
expect(topic.reload.public_topic_timer).to eq(nil)
|
expect(topic.reload.public_topic_timer).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,53 +4,40 @@ RSpec.describe Jobs::CheckNewFeatures do
|
||||||
def build_feature_hash(id:, created_at:, discourse_version: "2.9.0.beta10")
|
def build_feature_hash(id:, created_at:, discourse_version: "2.9.0.beta10")
|
||||||
{
|
{
|
||||||
id: id,
|
id: id,
|
||||||
user_id: 89432,
|
user_id: 89_432,
|
||||||
emoji: "👤",
|
emoji: "👤",
|
||||||
title: "New fancy feature!",
|
title: "New fancy feature!",
|
||||||
description: "",
|
description: "",
|
||||||
link: "https://meta.discourse.org/t/-/238821",
|
link: "https://meta.discourse.org/t/-/238821",
|
||||||
created_at: created_at.iso8601,
|
created_at: created_at.iso8601,
|
||||||
updated_at: (created_at + 1.minutes).iso8601,
|
updated_at: (created_at + 1.minutes).iso8601,
|
||||||
discourse_version: discourse_version
|
discourse_version: discourse_version,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def stub_meta_new_features_endpoint(*features)
|
def stub_meta_new_features_endpoint(*features)
|
||||||
stub_request(:get, "https://meta.discourse.org/new-features.json")
|
stub_request(:get, "https://meta.discourse.org/new-features.json").to_return(
|
||||||
.to_return(
|
status: 200,
|
||||||
status: 200,
|
body: JSON.dump(features),
|
||||||
body: JSON.dump(features),
|
headers: {
|
||||||
headers: {
|
"Content-Type" => "application/json",
|
||||||
"Content-Type" => "application/json"
|
},
|
||||||
}
|
)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
fab!(:admin1) { Fabricate(:admin) }
|
fab!(:admin1) { Fabricate(:admin) }
|
||||||
fab!(:admin2) { Fabricate(:admin) }
|
fab!(:admin2) { Fabricate(:admin) }
|
||||||
|
|
||||||
let(:feature1) do
|
let(:feature1) do
|
||||||
build_feature_hash(
|
build_feature_hash(id: 35, created_at: 3.days.ago, discourse_version: "2.8.1.beta12")
|
||||||
id: 35,
|
|
||||||
created_at: 3.days.ago,
|
|
||||||
discourse_version: "2.8.1.beta12"
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:feature2) do
|
let(:feature2) do
|
||||||
build_feature_hash(
|
build_feature_hash(id: 34, created_at: 2.days.ago, discourse_version: "2.8.1.beta13")
|
||||||
id: 34,
|
|
||||||
created_at: 2.days.ago,
|
|
||||||
discourse_version: "2.8.1.beta13"
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:pending_feature) do
|
let(:pending_feature) do
|
||||||
build_feature_hash(
|
build_feature_hash(id: 37, created_at: 1.day.ago, discourse_version: "2.8.1.beta14")
|
||||||
id: 37,
|
|
||||||
created_at: 1.day.ago,
|
|
||||||
discourse_version: "2.8.1.beta14"
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -59,9 +46,7 @@ RSpec.describe Jobs::CheckNewFeatures do
|
||||||
stub_meta_new_features_endpoint(feature1, feature2, pending_feature)
|
stub_meta_new_features_endpoint(feature1, feature2, pending_feature)
|
||||||
end
|
end
|
||||||
|
|
||||||
after do
|
after { DiscourseUpdates.clean_state }
|
||||||
DiscourseUpdates.clean_state
|
|
||||||
end
|
|
||||||
|
|
||||||
it "backfills last viewed feature for admins who don't have last viewed feature" do
|
it "backfills last viewed feature for admins who don't have last viewed feature" do
|
||||||
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta12")
|
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta12")
|
||||||
|
@ -70,8 +55,12 @@ RSpec.describe Jobs::CheckNewFeatures do
|
||||||
|
|
||||||
described_class.new.execute({})
|
described_class.new.execute({})
|
||||||
|
|
||||||
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id).iso8601).to eq(feature1[:created_at])
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin2.id).iso8601).to eq(
|
||||||
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id).iso8601).to eq(Time.zone.now.iso8601)
|
feature1[:created_at],
|
||||||
|
)
|
||||||
|
expect(DiscourseUpdates.get_last_viewed_feature_date(admin1.id).iso8601).to eq(
|
||||||
|
Time.zone.now.iso8601,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "notifies admins about new features that are available in the site's version" do
|
it "notifies admins about new features that are available in the site's version" do
|
||||||
|
@ -79,14 +68,18 @@ RSpec.describe Jobs::CheckNewFeatures do
|
||||||
|
|
||||||
described_class.new.execute({})
|
described_class.new.execute({})
|
||||||
|
|
||||||
expect(admin1.notifications.where(
|
expect(
|
||||||
notification_type: Notification.types[:new_features],
|
admin1
|
||||||
read: false
|
.notifications
|
||||||
).count).to eq(1)
|
.where(notification_type: Notification.types[:new_features], read: false)
|
||||||
expect(admin2.notifications.where(
|
.count,
|
||||||
notification_type: Notification.types[:new_features],
|
).to eq(1)
|
||||||
read: false
|
expect(
|
||||||
).count).to eq(1)
|
admin2
|
||||||
|
.notifications
|
||||||
|
.where(notification_type: Notification.types[:new_features], read: false)
|
||||||
|
.count,
|
||||||
|
).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "consolidates new features notifications" do
|
it "consolidates new features notifications" do
|
||||||
|
@ -94,10 +87,11 @@ RSpec.describe Jobs::CheckNewFeatures do
|
||||||
|
|
||||||
described_class.new.execute({})
|
described_class.new.execute({})
|
||||||
|
|
||||||
notification = admin1.notifications.where(
|
notification =
|
||||||
notification_type: Notification.types[:new_features],
|
admin1
|
||||||
read: false
|
.notifications
|
||||||
).first
|
.where(notification_type: Notification.types[:new_features], read: false)
|
||||||
|
.first
|
||||||
expect(notification).to be_present
|
expect(notification).to be_present
|
||||||
|
|
||||||
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta14")
|
DiscourseUpdates.stubs(:current_version).returns("2.8.1.beta14")
|
||||||
|
@ -106,10 +100,11 @@ RSpec.describe Jobs::CheckNewFeatures do
|
||||||
# old notification is destroyed
|
# old notification is destroyed
|
||||||
expect(Notification.find_by(id: notification.id)).to eq(nil)
|
expect(Notification.find_by(id: notification.id)).to eq(nil)
|
||||||
|
|
||||||
notification = admin1.notifications.where(
|
notification =
|
||||||
notification_type: Notification.types[:new_features],
|
admin1
|
||||||
read: false
|
.notifications
|
||||||
).first
|
.where(notification_type: Notification.types[:new_features], read: false)
|
||||||
|
.first
|
||||||
# new notification is created
|
# new notification is created
|
||||||
expect(notification).to be_present
|
expect(notification).to be_present
|
||||||
end
|
end
|
||||||
|
@ -121,6 +116,8 @@ RSpec.describe Jobs::CheckNewFeatures do
|
||||||
described_class.new.execute({})
|
described_class.new.execute({})
|
||||||
|
|
||||||
expect(admin1.notifications.count).to eq(0)
|
expect(admin1.notifications.count).to eq(0)
|
||||||
expect(admin2.notifications.where(notification_type: Notification.types[:new_features]).count).to eq(1)
|
expect(
|
||||||
|
admin2.notifications.where(notification_type: Notification.types[:new_features]).count,
|
||||||
|
).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,13 +5,13 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
|
||||||
fab!(:topic) { Fabricate(:topic, created_at: 5.hours.ago) }
|
fab!(:topic) { Fabricate(:topic, created_at: 5.hours.ago) }
|
||||||
fab!(:dismissed_topic_user) { Fabricate(:dismissed_topic_user, user: user, topic: topic) }
|
fab!(:dismissed_topic_user) { Fabricate(:dismissed_topic_user, user: user, topic: topic) }
|
||||||
|
|
||||||
describe '#delete_overdue_dismissals!' do
|
describe "#delete_overdue_dismissals!" do
|
||||||
it 'does not delete when new_topic_duration_minutes is set to always' do
|
it "does not delete when new_topic_duration_minutes is set to always" do
|
||||||
user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
|
user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
|
||||||
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
|
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes when new_topic_duration_minutes is set to since last visit' do
|
it "deletes when new_topic_duration_minutes is set to since last visit" do
|
||||||
user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::LAST_VISIT)
|
user.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::LAST_VISIT)
|
||||||
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
|
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
|
||||||
expect { described_class.new.execute({}) }.to change { DismissedTopicUser.count }.by(-1)
|
expect { described_class.new.execute({}) }.to change { DismissedTopicUser.count }.by(-1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes when new_topic_duration_minutes is set to created in the last day' do
|
it "deletes when new_topic_duration_minutes is set to created in the last day" do
|
||||||
user.user_option.update(new_topic_duration_minutes: 1440)
|
user.user_option.update(new_topic_duration_minutes: 1440)
|
||||||
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
|
expect { described_class.new.execute({}) }.not_to change { DismissedTopicUser.count }
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#delete_over_the_limit_dismissals!' do
|
describe "#delete_over_the_limit_dismissals!" do
|
||||||
fab!(:user2) { Fabricate(:user, created_at: 1.days.ago, previous_visit_at: 1.days.ago) }
|
fab!(:user2) { Fabricate(:user, created_at: 1.days.ago, previous_visit_at: 1.days.ago) }
|
||||||
fab!(:topic2) { Fabricate(:topic, created_at: 6.hours.ago) }
|
fab!(:topic2) { Fabricate(:topic, created_at: 6.hours.ago) }
|
||||||
fab!(:topic3) { Fabricate(:topic, created_at: 2.hours.ago) }
|
fab!(:topic3) { Fabricate(:topic, created_at: 2.hours.ago) }
|
||||||
|
@ -41,7 +41,7 @@ RSpec.describe Jobs::CleanDismissedTopicUsers do
|
||||||
user2.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
|
user2.user_option.update(new_topic_duration_minutes: User::NewTopicDuration::ALWAYS)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes over the limit dismissals' do
|
it "deletes over the limit dismissals" do
|
||||||
described_class.new.execute({})
|
described_class.new.execute({})
|
||||||
expect(dismissed_topic_user.reload).to be_present
|
expect(dismissed_topic_user.reload).to be_present
|
||||||
expect(dismissed_topic_user2.reload).to be_present
|
expect(dismissed_topic_user2.reload).to be_present
|
||||||
|
|
|
@ -6,12 +6,27 @@ RSpec.describe Jobs::CleanUpAssociatedAccounts do
|
||||||
it "deletes the correct records" do
|
it "deletes the correct records" do
|
||||||
freeze_time
|
freeze_time
|
||||||
|
|
||||||
last_week = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "1", updated_at: 7.days.ago)
|
last_week =
|
||||||
today = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "12", updated_at: 12.hours.ago)
|
UserAssociatedAccount.create!(
|
||||||
connected = UserAssociatedAccount.create!(provider_name: "twitter", provider_uid: "123", user: Fabricate(:user), updated_at: 12.hours.ago)
|
provider_name: "twitter",
|
||||||
|
provider_uid: "1",
|
||||||
|
updated_at: 7.days.ago,
|
||||||
|
)
|
||||||
|
today =
|
||||||
|
UserAssociatedAccount.create!(
|
||||||
|
provider_name: "twitter",
|
||||||
|
provider_uid: "12",
|
||||||
|
updated_at: 12.hours.ago,
|
||||||
|
)
|
||||||
|
connected =
|
||||||
|
UserAssociatedAccount.create!(
|
||||||
|
provider_name: "twitter",
|
||||||
|
provider_uid: "123",
|
||||||
|
user: Fabricate(:user),
|
||||||
|
updated_at: 12.hours.ago,
|
||||||
|
)
|
||||||
|
|
||||||
expect { subject }.to change { UserAssociatedAccount.count }.by(-1)
|
expect { subject }.to change { UserAssociatedAccount.count }.by(-1)
|
||||||
expect(UserAssociatedAccount.all).to contain_exactly(today, connected)
|
expect(UserAssociatedAccount.all).to contain_exactly(today, connected)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,9 +5,7 @@ RSpec.describe Jobs::CleanUpEmailLogs do
|
||||||
fab!(:email_log2) { Fabricate(:email_log, created_at: 2.weeks.ago) }
|
fab!(:email_log2) { Fabricate(:email_log, created_at: 2.weeks.ago) }
|
||||||
fab!(:email_log3) { Fabricate(:email_log, created_at: 2.days.ago) }
|
fab!(:email_log3) { Fabricate(:email_log, created_at: 2.days.ago) }
|
||||||
|
|
||||||
let!(:skipped_email_log) do
|
let!(:skipped_email_log) { Fabricate(:skipped_email_log, created_at: 2.years.ago) }
|
||||||
Fabricate(:skipped_email_log, created_at: 2.years.ago)
|
|
||||||
end
|
|
||||||
|
|
||||||
fab!(:skipped_email_log2) { Fabricate(:skipped_email_log) }
|
fab!(:skipped_email_log2) { Fabricate(:skipped_email_log) }
|
||||||
|
|
||||||
|
@ -23,10 +21,6 @@ RSpec.describe Jobs::CleanUpEmailLogs do
|
||||||
|
|
||||||
expect(EmailLog.all).to contain_exactly(email_log, email_log2, email_log3)
|
expect(EmailLog.all).to contain_exactly(email_log, email_log2, email_log3)
|
||||||
|
|
||||||
expect(SkippedEmailLog.all).to contain_exactly(
|
expect(SkippedEmailLog.all).to contain_exactly(skipped_email_log, skipped_email_log2)
|
||||||
skipped_email_log,
|
|
||||||
skipped_email_log2
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,22 +4,16 @@ RSpec.describe Jobs::CleanUpInactiveUsers do
|
||||||
it "should clean up new users that have been inactive" do
|
it "should clean up new users that have been inactive" do
|
||||||
SiteSetting.clean_up_inactive_users_after_days = 0
|
SiteSetting.clean_up_inactive_users_after_days = 0
|
||||||
|
|
||||||
user = Fabricate(:user,
|
user = Fabricate(:user, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser])
|
||||||
last_seen_at: 5.days.ago,
|
|
||||||
trust_level: TrustLevel.levels[:newuser]
|
|
||||||
)
|
|
||||||
|
|
||||||
Fabricate(:active_user)
|
Fabricate(:active_user)
|
||||||
|
|
||||||
Fabricate(:post, user: Fabricate(:user,
|
Fabricate(
|
||||||
trust_level: TrustLevel.levels[:newuser],
|
:post,
|
||||||
last_seen_at: 5.days.ago
|
user: Fabricate(:user, trust_level: TrustLevel.levels[:newuser], last_seen_at: 5.days.ago),
|
||||||
)).user
|
).user
|
||||||
|
|
||||||
Fabricate(:user,
|
Fabricate(:user, trust_level: TrustLevel.levels[:newuser], last_seen_at: 2.days.ago)
|
||||||
trust_level: TrustLevel.levels[:newuser],
|
|
||||||
last_seen_at: 2.days.ago
|
|
||||||
)
|
|
||||||
|
|
||||||
Fabricate(:user, trust_level: TrustLevel.levels[:basic])
|
Fabricate(:user, trust_level: TrustLevel.levels[:basic])
|
||||||
|
|
||||||
|
@ -27,8 +21,7 @@ RSpec.describe Jobs::CleanUpInactiveUsers do
|
||||||
|
|
||||||
SiteSetting.clean_up_inactive_users_after_days = 4
|
SiteSetting.clean_up_inactive_users_after_days = 4
|
||||||
|
|
||||||
expect { described_class.new.execute({}) }
|
expect { described_class.new.execute({}) }.to change { User.count }.by(-1)
|
||||||
.to change { User.count }.by(-1)
|
|
||||||
|
|
||||||
expect(User.exists?(id: user.id)).to eq(false)
|
expect(User.exists?(id: user.id)).to eq(false)
|
||||||
end
|
end
|
||||||
|
@ -43,7 +36,8 @@ RSpec.describe Jobs::CleanUpInactiveUsers do
|
||||||
|
|
||||||
it "doesn't delete inactive mods" do
|
it "doesn't delete inactive mods" do
|
||||||
SiteSetting.clean_up_inactive_users_after_days = 4
|
SiteSetting.clean_up_inactive_users_after_days = 4
|
||||||
moderator = Fabricate(:moderator, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser])
|
moderator =
|
||||||
|
Fabricate(:moderator, last_seen_at: 5.days.ago, trust_level: TrustLevel.levels[:newuser])
|
||||||
|
|
||||||
expect { described_class.new.execute({}) }.to_not change { User.count }
|
expect { described_class.new.execute({}) }.to_not change { User.count }
|
||||||
expect(User.exists?(moderator.id)).to eq(true)
|
expect(User.exists?(moderator.id)).to eq(true)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Jobs::CleanUpPostReplyKeys do
|
RSpec.describe Jobs::CleanUpPostReplyKeys do
|
||||||
it 'removes old post_reply_keys' do
|
it "removes old post_reply_keys" do
|
||||||
freeze_time
|
freeze_time
|
||||||
|
|
||||||
reply_key1 = Fabricate(:post_reply_key, created_at: 1.day.ago)
|
reply_key1 = Fabricate(:post_reply_key, created_at: 1.day.ago)
|
||||||
|
@ -10,16 +10,12 @@ RSpec.describe Jobs::CleanUpPostReplyKeys do
|
||||||
|
|
||||||
SiteSetting.disallow_reply_by_email_after_days = 0
|
SiteSetting.disallow_reply_by_email_after_days = 0
|
||||||
|
|
||||||
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }
|
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }.not_to change { PostReplyKey.count }
|
||||||
.not_to change { PostReplyKey.count }
|
|
||||||
|
|
||||||
SiteSetting.disallow_reply_by_email_after_days = 2
|
SiteSetting.disallow_reply_by_email_after_days = 2
|
||||||
|
|
||||||
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }
|
expect { Jobs::CleanUpPostReplyKeys.new.execute({}) }.to change { PostReplyKey.count }.by(-1)
|
||||||
.to change { PostReplyKey.count }.by(-1)
|
|
||||||
|
|
||||||
expect(PostReplyKey.all).to contain_exactly(
|
expect(PostReplyKey.all).to contain_exactly(reply_key1, reply_key2)
|
||||||
reply_key1, reply_key2
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,7 +37,7 @@ RSpec.describe Jobs::CleanUpUnusedStagedUsers do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when staged user is not old enough' do
|
context "when staged user is not old enough" do
|
||||||
before { staged_user.update!(created_at: 5.months.ago) }
|
before { staged_user.update!(created_at: 5.months.ago) }
|
||||||
include_examples "does not delete"
|
include_examples "does not delete"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Jobs::CleanUpUploads do
|
RSpec.describe Jobs::CleanUpUploads do
|
||||||
|
|
||||||
def fabricate_upload(attributes = {})
|
def fabricate_upload(attributes = {})
|
||||||
Fabricate(:upload, { created_at: 2.hours.ago }.merge(attributes))
|
Fabricate(:upload, { created_at: 2.hours.ago }.merge(attributes))
|
||||||
end
|
end
|
||||||
|
@ -22,7 +21,6 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "only runs upload cleanup every grace period / 2 time" do
|
it "only runs upload cleanup every grace period / 2 time" do
|
||||||
|
|
||||||
SiteSetting.clean_orphan_uploads_grace_period_hours = 48
|
SiteSetting.clean_orphan_uploads_grace_period_hours = 48
|
||||||
expired = fabricate_upload(created_at: 49.hours.ago)
|
expired = fabricate_upload(created_at: 49.hours.ago)
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
|
@ -38,44 +36,31 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
|
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
expect(Upload.exists?(id: upload.id)).to eq(false)
|
expect(Upload.exists?(id: upload.id)).to eq(false)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes orphan uploads" do
|
it "deletes orphan uploads" do
|
||||||
expect do
|
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
|
||||||
end.to change { Upload.count }.by(-1)
|
|
||||||
|
|
||||||
expect(Upload.exists?(id: expired_upload.id)).to eq(false)
|
expect(Upload.exists?(id: expired_upload.id)).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'unused callbacks' do
|
describe "unused callbacks" do
|
||||||
before do
|
before { Upload.add_unused_callback { |uploads| uploads.where.not(id: expired_upload.id) } }
|
||||||
Upload.add_unused_callback do |uploads|
|
|
||||||
uploads.where.not(id: expired_upload.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
after { Upload.reset_unused_callbacks }
|
||||||
Upload.reset_unused_callbacks
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete uploads skipped by an unused callback' do
|
it "does not delete uploads skipped by an unused callback" do
|
||||||
expect do
|
expect do Jobs::CleanUpUploads.new.execute(nil) end.not_to change { Upload.count }
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
|
||||||
end.not_to change { Upload.count }
|
|
||||||
|
|
||||||
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes other uploads not skipped by an unused callback' do
|
it "deletes other uploads not skipped by an unused callback" do
|
||||||
expired_upload2 = fabricate_upload
|
expired_upload2 = fabricate_upload
|
||||||
upload = fabricate_upload
|
upload = fabricate_upload
|
||||||
UploadReference.create(target: Fabricate(:post), upload: upload)
|
UploadReference.create(target: Fabricate(:post), upload: upload)
|
||||||
|
|
||||||
expect do
|
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
|
||||||
end.to change { Upload.count }.by(-1)
|
|
||||||
|
|
||||||
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
||||||
expect(Upload.exists?(id: expired_upload2.id)).to eq(false)
|
expect(Upload.exists?(id: expired_upload2.id)).to eq(false)
|
||||||
|
@ -83,33 +68,23 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'in use callbacks' do
|
describe "in use callbacks" do
|
||||||
before do
|
before { Upload.add_in_use_callback { |upload| expired_upload.id == upload.id } }
|
||||||
Upload.add_in_use_callback do |upload|
|
|
||||||
expired_upload.id == upload.id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
after { Upload.reset_in_use_callbacks }
|
||||||
Upload.reset_in_use_callbacks
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete uploads that are in use by callback' do
|
it "does not delete uploads that are in use by callback" do
|
||||||
expect do
|
expect do Jobs::CleanUpUploads.new.execute(nil) end.not_to change { Upload.count }
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
|
||||||
end.not_to change { Upload.count }
|
|
||||||
|
|
||||||
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deletes other uploads that are not in use by callback' do
|
it "deletes other uploads that are not in use by callback" do
|
||||||
expired_upload2 = fabricate_upload
|
expired_upload2 = fabricate_upload
|
||||||
upload = fabricate_upload
|
upload = fabricate_upload
|
||||||
UploadReference.create(target: Fabricate(:post), upload: upload)
|
UploadReference.create(target: Fabricate(:post), upload: upload)
|
||||||
|
|
||||||
expect do
|
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
|
||||||
end.to change { Upload.count }.by(-1)
|
|
||||||
|
|
||||||
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
||||||
expect(Upload.exists?(id: expired_upload2.id)).to eq(false)
|
expect(Upload.exists?(id: expired_upload2.id)).to eq(false)
|
||||||
|
@ -117,27 +92,20 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when clean_up_uploads is disabled' do
|
describe "when clean_up_uploads is disabled" do
|
||||||
before do
|
before { SiteSetting.clean_up_uploads = false }
|
||||||
SiteSetting.clean_up_uploads = false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should still delete invalid upload records' do
|
it "should still delete invalid upload records" do
|
||||||
upload2 = fabricate_upload(
|
upload2 = fabricate_upload(url: "", retain_hours: nil)
|
||||||
url: "",
|
|
||||||
retain_hours: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
expect do
|
expect do Jobs::CleanUpUploads.new.execute(nil) end.to change { Upload.count }.by(-1)
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
|
||||||
end.to change { Upload.count }.by(-1)
|
|
||||||
|
|
||||||
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
expect(Upload.exists?(id: expired_upload.id)).to eq(true)
|
||||||
expect(Upload.exists?(id: upload2.id)).to eq(false)
|
expect(Upload.exists?(id: upload2.id)).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not clean up upload site settings' do
|
it "does not clean up upload site settings" do
|
||||||
begin
|
begin
|
||||||
original_provider = SiteSetting.provider
|
original_provider = SiteSetting.provider
|
||||||
SiteSetting.provider = SiteSettings::DbProvider.new(SiteSetting)
|
SiteSetting.provider = SiteSettings::DbProvider.new(SiteSetting)
|
||||||
|
@ -161,8 +129,7 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
SiteSetting.large_icon = large_icon_upload
|
SiteSetting.large_icon = large_icon_upload
|
||||||
SiteSetting.opengraph_image = opengraph_image_upload
|
SiteSetting.opengraph_image = opengraph_image_upload
|
||||||
|
|
||||||
SiteSetting.twitter_summary_large_image =
|
SiteSetting.twitter_summary_large_image = twitter_summary_large_image_upload
|
||||||
twitter_summary_large_image_upload
|
|
||||||
|
|
||||||
SiteSetting.favicon = favicon_upload
|
SiteSetting.favicon = favicon_upload
|
||||||
SiteSetting.apple_touch_icon = apple_touch_icon_upload
|
SiteSetting.apple_touch_icon = apple_touch_icon_upload
|
||||||
|
@ -170,20 +137,13 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
|
|
||||||
[
|
[
|
||||||
logo_upload,
|
logo_upload, logo_small_upload, digest_logo_upload, mobile_logo_upload, large_icon_upload,
|
||||||
logo_small_upload,
|
opengraph_image_upload, twitter_summary_large_image_upload, favicon_upload,
|
||||||
digest_logo_upload,
|
apple_touch_icon_upload, system_upload,
|
||||||
mobile_logo_upload,
|
|
||||||
large_icon_upload,
|
|
||||||
opengraph_image_upload,
|
|
||||||
twitter_summary_large_image_upload,
|
|
||||||
favicon_upload,
|
|
||||||
apple_touch_icon_upload,
|
|
||||||
system_upload
|
|
||||||
].each { |record| expect(Upload.exists?(id: record.id)).to eq(true) }
|
].each { |record| expect(Upload.exists?(id: record.id)).to eq(true) }
|
||||||
|
|
||||||
fabricate_upload
|
fabricate_upload
|
||||||
SiteSetting.opengraph_image = ''
|
SiteSetting.opengraph_image = ""
|
||||||
|
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
ensure
|
ensure
|
||||||
|
@ -307,15 +267,19 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
upload2 = fabricate_upload
|
upload2 = fabricate_upload
|
||||||
upload3 = fabricate_upload
|
upload3 = fabricate_upload
|
||||||
|
|
||||||
Fabricate(:reviewable_queued_post_topic, payload: {
|
Fabricate(
|
||||||
raw: "#{upload.short_url}\n#{upload2.short_url}"
|
:reviewable_queued_post_topic,
|
||||||
})
|
|
||||||
|
|
||||||
Fabricate(:reviewable_queued_post_topic,
|
|
||||||
payload: {
|
payload: {
|
||||||
raw: "#{upload3.short_url}"
|
raw: "#{upload.short_url}\n#{upload2.short_url}",
|
||||||
},
|
},
|
||||||
status: Reviewable.statuses[:rejected]
|
)
|
||||||
|
|
||||||
|
Fabricate(
|
||||||
|
:reviewable_queued_post_topic,
|
||||||
|
payload: {
|
||||||
|
raw: "#{upload3.short_url}",
|
||||||
|
},
|
||||||
|
status: Reviewable.statuses[:rejected],
|
||||||
)
|
)
|
||||||
|
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
|
@ -350,7 +314,7 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
|
|
||||||
it "does not delete custom emojis" do
|
it "does not delete custom emojis" do
|
||||||
upload = fabricate_upload
|
upload = fabricate_upload
|
||||||
CustomEmoji.create!(name: 'test', upload: upload)
|
CustomEmoji.create!(name: "test", upload: upload)
|
||||||
|
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
|
|
||||||
|
@ -371,7 +335,12 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
it "does not delete theme setting uploads" do
|
it "does not delete theme setting uploads" do
|
||||||
theme = Fabricate(:theme)
|
theme = Fabricate(:theme)
|
||||||
theme_upload = fabricate_upload
|
theme_upload = fabricate_upload
|
||||||
ThemeSetting.create!(theme: theme, data_type: ThemeSetting.types[:upload], value: theme_upload.id.to_s, name: "my_setting_name")
|
ThemeSetting.create!(
|
||||||
|
theme: theme,
|
||||||
|
data_type: ThemeSetting.types[:upload],
|
||||||
|
value: theme_upload.id.to_s,
|
||||||
|
name: "my_setting_name",
|
||||||
|
)
|
||||||
|
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
|
|
||||||
|
@ -390,10 +359,30 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "deletes external upload stubs that have expired" do
|
it "deletes external upload stubs that have expired" do
|
||||||
external_stub1 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:created], created_at: 10.minutes.ago)
|
external_stub1 =
|
||||||
external_stub2 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:created], created_at: (ExternalUploadStub::CREATED_EXPIRY_HOURS.hours + 10.minutes).ago)
|
Fabricate(
|
||||||
external_stub3 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:uploaded], created_at: 10.minutes.ago)
|
:external_upload_stub,
|
||||||
external_stub4 = Fabricate(:external_upload_stub, status: ExternalUploadStub.statuses[:uploaded], created_at: (ExternalUploadStub::UPLOADED_EXPIRY_HOURS.hours + 10.minutes).ago)
|
status: ExternalUploadStub.statuses[:created],
|
||||||
|
created_at: 10.minutes.ago,
|
||||||
|
)
|
||||||
|
external_stub2 =
|
||||||
|
Fabricate(
|
||||||
|
:external_upload_stub,
|
||||||
|
status: ExternalUploadStub.statuses[:created],
|
||||||
|
created_at: (ExternalUploadStub::CREATED_EXPIRY_HOURS.hours + 10.minutes).ago,
|
||||||
|
)
|
||||||
|
external_stub3 =
|
||||||
|
Fabricate(
|
||||||
|
:external_upload_stub,
|
||||||
|
status: ExternalUploadStub.statuses[:uploaded],
|
||||||
|
created_at: 10.minutes.ago,
|
||||||
|
)
|
||||||
|
external_stub4 =
|
||||||
|
Fabricate(
|
||||||
|
:external_upload_stub,
|
||||||
|
status: ExternalUploadStub.statuses[:uploaded],
|
||||||
|
created_at: (ExternalUploadStub::UPLOADED_EXPIRY_HOURS.hours + 10.minutes).ago,
|
||||||
|
)
|
||||||
Jobs::CleanUpUploads.new.execute(nil)
|
Jobs::CleanUpUploads.new.execute(nil)
|
||||||
expect(ExternalUploadStub.pluck(:id)).to contain_exactly(external_stub1.id, external_stub3.id)
|
expect(ExternalUploadStub.pluck(:id)).to contain_exactly(external_stub1.id, external_stub3.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,27 +3,29 @@
|
||||||
RSpec.describe Jobs::CleanUpUserExportTopics do
|
RSpec.describe Jobs::CleanUpUserExportTopics do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
it 'should delete ancient user export system messages' do
|
it "should delete ancient user export system messages" do
|
||||||
post_en = SystemMessage.create_from_system_user(
|
post_en =
|
||||||
user,
|
SystemMessage.create_from_system_user(
|
||||||
:csv_export_succeeded,
|
user,
|
||||||
download_link: "http://example.com/download",
|
:csv_export_succeeded,
|
||||||
file_name: "xyz_en.gz",
|
download_link: "http://example.com/download",
|
||||||
file_size: "55",
|
file_name: "xyz_en.gz",
|
||||||
export_title: "user_archive"
|
file_size: "55",
|
||||||
)
|
export_title: "user_archive",
|
||||||
|
)
|
||||||
topic_en = post_en.topic
|
topic_en = post_en.topic
|
||||||
topic_en.update!(created_at: 5.days.ago)
|
topic_en.update!(created_at: 5.days.ago)
|
||||||
|
|
||||||
I18n.locale = :fr
|
I18n.locale = :fr
|
||||||
post_fr = SystemMessage.create_from_system_user(
|
post_fr =
|
||||||
user,
|
SystemMessage.create_from_system_user(
|
||||||
:csv_export_succeeded,
|
user,
|
||||||
download_link: "http://example.com/download",
|
:csv_export_succeeded,
|
||||||
file_name: "xyz_fr.gz",
|
download_link: "http://example.com/download",
|
||||||
file_size: "56",
|
file_name: "xyz_fr.gz",
|
||||||
export_title: "user_archive"
|
file_size: "56",
|
||||||
)
|
export_title: "user_archive",
|
||||||
|
)
|
||||||
topic_fr = post_fr.topic
|
topic_fr = post_fr.topic
|
||||||
topic_fr.update!(created_at: 5.days.ago)
|
topic_fr.update!(created_at: 5.days.ago)
|
||||||
|
|
||||||
|
|
|
@ -3,22 +3,15 @@
|
||||||
RSpec.describe Jobs::CloseTopic do
|
RSpec.describe Jobs::CloseTopic do
|
||||||
fab!(:admin) { Fabricate(:admin) }
|
fab!(:admin) { Fabricate(:admin) }
|
||||||
|
|
||||||
fab!(:topic) do
|
fab!(:topic) { Fabricate(:topic_timer, user: admin).topic }
|
||||||
Fabricate(:topic_timer, user: admin).topic
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should be able to close a topic' do
|
it "should be able to close a topic" do
|
||||||
freeze_time(61.minutes.from_now) do
|
freeze_time(61.minutes.from_now) do
|
||||||
described_class.new.execute(
|
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
|
||||||
topic_timer_id: topic.public_topic_timer.id,
|
|
||||||
state: true
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(topic.reload.closed).to eq(true)
|
expect(topic.reload.closed).to eq(true)
|
||||||
|
|
||||||
expect(Post.last.raw).to eq(I18n.t(
|
expect(Post.last.raw).to eq(I18n.t("topic_statuses.autoclosed_enabled_minutes", count: 61))
|
||||||
'topic_statuses.autoclosed_enabled_minutes', count: 61
|
|
||||||
))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,61 +23,47 @@ RSpec.describe Jobs::CloseTopic do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when trying to close a topic that has already been closed' do
|
describe "when trying to close a topic that has already been closed" do
|
||||||
it 'should delete the topic timer' do
|
it "should delete the topic timer" do
|
||||||
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
||||||
|
|
||||||
topic.update!(closed: true)
|
topic.update!(closed: true)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
described_class.new.execute(
|
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
|
||||||
topic_timer_id: topic.public_topic_timer.id,
|
|
||||||
state: true
|
|
||||||
)
|
|
||||||
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
|
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when trying to close a topic that has been deleted' do
|
describe "when trying to close a topic that has been deleted" do
|
||||||
it 'should delete the topic timer' do
|
it "should delete the topic timer" do
|
||||||
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
||||||
|
|
||||||
topic.trash!
|
topic.trash!
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
described_class.new.execute(
|
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
|
||||||
topic_timer_id: topic.public_topic_timer.id,
|
|
||||||
state: true
|
|
||||||
)
|
|
||||||
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
|
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when user is no longer authorized to close topics' do
|
describe "when user is no longer authorized to close topics" do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
fab!(:topic) do
|
fab!(:topic) { Fabricate(:topic_timer, user: user).topic }
|
||||||
Fabricate(:topic_timer, user: user).topic
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should destroy the topic timer' do
|
it "should destroy the topic timer" do
|
||||||
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
described_class.new.execute(
|
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
|
||||||
topic_timer_id: topic.public_topic_timer.id,
|
|
||||||
state: true
|
|
||||||
)
|
|
||||||
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
|
end.to change { TopicTimer.exists?(topic_id: topic.id) }.from(true).to(false)
|
||||||
|
|
||||||
expect(topic.reload.closed).to eq(false)
|
expect(topic.reload.closed).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should reconfigure topic timer if category's topics are set to autoclose" do
|
it "should reconfigure topic timer if category's topics are set to autoclose" do
|
||||||
category = Fabricate(:category,
|
category = Fabricate(:category, auto_close_based_on_last_post: true, auto_close_hours: 5)
|
||||||
auto_close_based_on_last_post: true,
|
|
||||||
auto_close_hours: 5
|
|
||||||
)
|
|
||||||
|
|
||||||
topic = Fabricate(:topic, category: category)
|
topic = Fabricate(:topic, category: category)
|
||||||
topic.public_topic_timer.update!(user: user)
|
topic.public_topic_timer.update!(user: user)
|
||||||
|
@ -92,12 +71,10 @@ RSpec.describe Jobs::CloseTopic do
|
||||||
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
freeze_time(topic.public_topic_timer.execute_at + 1.minute)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
described_class.new.execute(
|
described_class.new.execute(topic_timer_id: topic.public_topic_timer.id, state: true)
|
||||||
topic_timer_id: topic.public_topic_timer.id,
|
end.to change { topic.reload.public_topic_timer.user }.from(user).to(
|
||||||
state: true
|
Discourse.system_user,
|
||||||
)
|
).and change { topic.public_topic_timer.id }
|
||||||
end.to change { topic.reload.public_topic_timer.user }.from(user).to(Discourse.system_user)
|
|
||||||
.and change { topic.public_topic_timer.id }
|
|
||||||
|
|
||||||
expect(topic.reload.closed).to eq(false)
|
expect(topic.reload.closed).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue