2019-04-29 20:27:42 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2022-07-27 22:27:38 -04:00
|
|
|
RSpec.describe StaticController do
|
2023-11-09 17:47:59 -05:00
|
|
|
fab!(:upload)
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2022-07-27 06:21:10 -04:00
|
|
|
describe "#favicon" do
|
2019-03-13 16:17:36 -04:00
|
|
|
let(:filename) { "smallest.png" }
|
|
|
|
let(:file) { file_from_fixtures(filename) }
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2019-03-13 16:17:36 -04:00
|
|
|
let(:upload) { UploadCreator.new(file, filename).create_for(Discourse.system_user.id) }
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2022-04-05 13:29:58 -04:00
|
|
|
after { Discourse.redis.scan_each(match: "memoize_*").each { |key| Discourse.redis.del(key) } }
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2022-07-27 12:14:14 -04:00
|
|
|
context "with local store" do
|
2019-03-13 16:17:36 -04:00
|
|
|
it "returns the default favicon if favicon has not been configured" do
|
|
|
|
get "/favicon/proxied"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
2019-09-11 20:41:50 -04:00
|
|
|
expect(response.media_type).to eq("image/png")
|
2019-05-01 09:44:45 -04:00
|
|
|
expect(response.body.bytesize).to eq(SiteIconManager.favicon.filesize)
|
2019-03-13 16:17:36 -04:00
|
|
|
end
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2019-03-13 16:17:36 -04:00
|
|
|
it "returns the configured favicon" do
|
|
|
|
SiteSetting.favicon = upload
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2019-03-13 16:17:36 -04:00
|
|
|
get "/favicon/proxied"
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2019-03-13 16:17:36 -04:00
|
|
|
expect(response.status).to eq(200)
|
2019-09-11 20:41:50 -04:00
|
|
|
expect(response.media_type).to eq("image/png")
|
2019-03-13 16:17:36 -04:00
|
|
|
expect(response.body.bytesize).to eq(upload.filesize)
|
|
|
|
end
|
2017-11-26 22:50:57 -05:00
|
|
|
end
|
|
|
|
|
2022-07-27 12:14:14 -04:00
|
|
|
context "with external store" do
|
2019-03-13 16:17:36 -04:00
|
|
|
let(:upload) do
|
|
|
|
Upload.create!(
|
|
|
|
url: "//s3-upload-bucket.s3-us-east-1.amazonaws.com/somewhere/a.png",
|
|
|
|
original_filename: filename,
|
|
|
|
filesize: file.size,
|
|
|
|
user_id: Discourse.system_user.id,
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2020-09-14 07:32:25 -04:00
|
|
|
before { setup_s3 }
|
2019-03-13 16:17:36 -04:00
|
|
|
|
|
|
|
it "can proxy a favicon correctly" do
|
|
|
|
SiteSetting.favicon = upload
|
|
|
|
|
|
|
|
stub_request(:get, "https:/#{upload.url}").to_return(status: 200, body: file)
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2019-03-13 16:17:36 -04:00
|
|
|
get "/favicon/proxied"
|
2017-11-26 22:50:57 -05:00
|
|
|
|
2019-03-13 16:17:36 -04:00
|
|
|
expect(response.status).to eq(200)
|
2019-09-11 20:41:50 -04:00
|
|
|
expect(response.media_type).to eq("image/png")
|
2019-03-13 16:17:36 -04:00
|
|
|
expect(response.body.bytesize).to eq(upload.filesize)
|
|
|
|
end
|
2024-05-22 21:29:08 -04:00
|
|
|
|
|
|
|
context "when favicon fails to load" do
|
|
|
|
before { FileHelper.stubs(:download).raises(SocketError) }
|
|
|
|
|
|
|
|
it "creates an admin notice" do
|
|
|
|
expect { get "/favicon/proxied" }.to change { AdminNotice.problem.count }.by(1)
|
|
|
|
end
|
|
|
|
end
|
2017-11-26 22:50:57 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 06:21:10 -04:00
|
|
|
describe "#cdn_asset" do
|
2023-12-06 17:25:00 -05:00
|
|
|
let(:site) { RailsMultisite::ConnectionManagement.current_db }
|
2019-07-16 10:05:37 -04:00
|
|
|
|
|
|
|
it "can serve assets" do
|
|
|
|
begin
|
|
|
|
assets_path = Rails.root.join("public/assets")
|
|
|
|
|
|
|
|
FileUtils.mkdir_p(assets_path)
|
|
|
|
|
|
|
|
file_path = assets_path.join("test.js.br")
|
|
|
|
File.write(file_path, "fake brotli file")
|
|
|
|
|
|
|
|
get "/cdn_asset/#{site}/test.js.br"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.headers["Cache-Control"]).to match(/public/)
|
|
|
|
ensure
|
|
|
|
File.delete(file_path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-27 06:21:10 -04:00
|
|
|
describe "#show" do
|
2014-07-24 14:27:34 -04:00
|
|
|
before do
|
|
|
|
post = create_post
|
2017-07-07 02:09:14 -04:00
|
|
|
SiteSetting.tos_topic_id = post.topic.id
|
|
|
|
SiteSetting.guidelines_topic_id = post.topic.id
|
|
|
|
SiteSetting.privacy_topic_id = post.topic.id
|
2014-07-24 14:27:34 -04:00
|
|
|
end
|
|
|
|
|
2013-10-30 16:37:22 -04:00
|
|
|
context "with a static file that's present" do
|
2019-03-03 22:32:12 -05:00
|
|
|
it "should return the right response for /faq" do
|
2017-08-31 00:06:56 -04:00
|
|
|
get "/faq"
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2018-06-07 04:11:09 -04:00
|
|
|
expect(response.status).to eq(200)
|
2017-08-31 00:06:56 -04:00
|
|
|
expect(response.body).to include(I18n.t("js.faq"))
|
2018-11-27 21:36:14 -05:00
|
|
|
expect(response.body).to include("<title>FAQ - Discourse</title>")
|
2013-07-23 14:42:52 -04:00
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2017-08-31 00:06:56 -04:00
|
|
|
[
|
2019-05-22 09:29:15 -04:00
|
|
|
["tos", :tos_url, I18n.t("js.tos")],
|
|
|
|
["privacy", :privacy_policy_url, I18n.t("js.privacy")],
|
2017-08-31 00:06:56 -04:00
|
|
|
].each do |id, setting_name, text|
|
2023-03-14 06:42:11 -04:00
|
|
|
context "with #{id}" do
|
2013-10-30 16:37:22 -04:00
|
|
|
context "when #{setting_name} site setting is NOT set" do
|
|
|
|
it "renders the #{id} page" do
|
2017-08-31 00:06:56 -04:00
|
|
|
get "/#{id}"
|
|
|
|
|
2018-06-07 04:11:09 -04:00
|
|
|
expect(response.status).to eq(200)
|
2017-08-31 00:06:56 -04:00
|
|
|
expect(response.body).to include(text)
|
2013-07-23 14:42:52 -04:00
|
|
|
end
|
2013-06-18 10:52:04 -04:00
|
|
|
end
|
|
|
|
|
2013-10-30 16:37:22 -04:00
|
|
|
context "when #{setting_name} site setting is set" do
|
2019-05-06 21:00:09 -04:00
|
|
|
before { SiteSetting.set(setting_name, "http://example.com/page") }
|
2013-06-18 10:52:04 -04:00
|
|
|
|
2013-10-30 16:37:22 -04:00
|
|
|
it "redirects to the #{setting_name}" do
|
2017-08-31 00:06:56 -04:00
|
|
|
get "/#{id}"
|
|
|
|
|
|
|
|
expect(response).to redirect_to("http://example.com/page")
|
2013-10-30 16:37:22 -04:00
|
|
|
end
|
2013-06-18 10:52:04 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-10-30 16:37:22 -04:00
|
|
|
context "with a missing file" do
|
|
|
|
it "should respond 404" do
|
2017-08-31 00:06:56 -04:00
|
|
|
get "/static/does-not-exist"
|
|
|
|
expect(response.status).to eq(404)
|
2013-10-30 16:37:22 -04:00
|
|
|
end
|
2019-03-04 04:34:48 -05:00
|
|
|
|
2022-07-27 12:14:14 -04:00
|
|
|
context "with modal pages" do
|
2019-03-04 04:34:48 -05:00
|
|
|
it "should return the right response for /signup" do
|
|
|
|
get "/signup"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should return the right response for /password-reset" do
|
|
|
|
get "/password-reset"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
end
|
|
|
|
end
|
2013-10-30 16:37:22 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "should redirect to / when logged in and path is /login" do
|
2017-08-31 00:06:56 -04:00
|
|
|
sign_in(Fabricate(:user))
|
|
|
|
get "/login"
|
|
|
|
expect(response).to redirect_to("/")
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2014-07-26 17:16:08 -04:00
|
|
|
it "should display the login template when login is required" do
|
2017-07-07 02:09:14 -04:00
|
|
|
SiteSetting.login_required = true
|
2017-08-31 00:06:56 -04:00
|
|
|
|
|
|
|
get "/login"
|
|
|
|
|
2018-06-07 04:11:09 -04:00
|
|
|
expect(response.status).to eq(200)
|
2017-08-31 00:06:56 -04:00
|
|
|
|
|
|
|
expect(response.body).to include(
|
|
|
|
PrettyText.cook(I18n.t("login_required.welcome_message", title: SiteSetting.title)),
|
|
|
|
)
|
2014-07-26 17:16:08 -04:00
|
|
|
end
|
2017-03-08 05:30:49 -05:00
|
|
|
|
|
|
|
context "when login_required is enabled" do
|
|
|
|
before { SiteSetting.login_required = true }
|
|
|
|
|
2018-12-18 16:40:05 -05:00
|
|
|
%w[faq guidelines rules conduct].each do |page_name|
|
2018-07-26 15:37:56 -04:00
|
|
|
it "#{page_name} page redirects to login page for anon" do
|
|
|
|
get "/#{page_name}"
|
|
|
|
expect(response).to redirect_to "/login"
|
|
|
|
end
|
2017-08-31 00:06:56 -04:00
|
|
|
|
2018-07-26 15:37:56 -04:00
|
|
|
it "#{page_name} page loads for logged in user" do
|
|
|
|
sign_in(Fabricate(:user))
|
2017-08-31 00:06:56 -04:00
|
|
|
|
2018-07-26 15:37:56 -04:00
|
|
|
get "/#{page_name}"
|
2017-08-31 00:06:56 -04:00
|
|
|
|
2018-07-26 15:37:56 -04:00
|
|
|
expect(response.status).to eq(200)
|
2019-05-22 09:29:15 -04:00
|
|
|
expect(response.body).to include(I18n.t("js.guidelines"))
|
2018-07-26 15:37:56 -04:00
|
|
|
end
|
2017-03-08 05:30:49 -05:00
|
|
|
end
|
|
|
|
end
|
2018-11-27 21:36:14 -05:00
|
|
|
|
2022-07-27 12:14:14 -04:00
|
|
|
context "with crawler view" do
|
2018-11-27 21:36:14 -05:00
|
|
|
it "should include correct title" do
|
|
|
|
get "/faq", headers: { "HTTP_USER_AGENT" => "Googlebot" }
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.body).to include("<title>FAQ - Discourse</title>")
|
|
|
|
end
|
|
|
|
end
|
2021-12-15 22:24:11 -05:00
|
|
|
|
2022-07-27 12:14:14 -04:00
|
|
|
context "with plugin api extensions" do
|
2021-12-15 22:24:11 -05:00
|
|
|
after do
|
|
|
|
Rails.application.reload_routes!
|
|
|
|
StaticController::CUSTOM_PAGES.clear
|
|
|
|
end
|
|
|
|
|
|
|
|
it "adds new topic-backed pages" do
|
|
|
|
routes = Proc.new { get "contact" => "static#show", :id => "contact" }
|
|
|
|
Discourse::Application.routes.send(:eval_block, routes)
|
|
|
|
|
|
|
|
topic_id = Fabricate(:post, cooked: "contact info").topic_id
|
2023-05-24 19:53:57 -04:00
|
|
|
SiteSetting.test_some_topic_id = topic_id
|
2021-12-15 22:24:11 -05:00
|
|
|
|
2023-05-24 19:53:57 -04:00
|
|
|
Plugin::Instance.new.add_topic_static_page("contact", topic_id: "test_some_topic_id")
|
2021-12-15 22:24:11 -05:00
|
|
|
|
|
|
|
get "/contact"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.body).to include("contact info")
|
|
|
|
end
|
|
|
|
|
|
|
|
it "replaces existing topic-backed pages" do
|
|
|
|
topic_id = Fabricate(:post, cooked: "Regular FAQ").topic_id
|
2023-05-24 19:53:57 -04:00
|
|
|
SiteSetting.test_some_topic_id = topic_id
|
|
|
|
|
2021-12-15 22:24:11 -05:00
|
|
|
polish_topic_id = Fabricate(:post, cooked: "Polish FAQ").topic_id
|
2023-05-24 19:53:57 -04:00
|
|
|
SiteSetting.test_some_other_topic_id = polish_topic_id
|
2021-12-15 22:24:11 -05:00
|
|
|
|
|
|
|
Plugin::Instance
|
|
|
|
.new
|
|
|
|
.add_topic_static_page("faq") do
|
2023-05-24 19:53:57 -04:00
|
|
|
current_user&.locale == "pl" ? "test_some_other_topic_id" : "test_some_topic_id"
|
2021-12-15 22:24:11 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
get "/faq"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.body).to include("Regular FAQ")
|
|
|
|
|
|
|
|
sign_in(Fabricate(:user, locale: "pl"))
|
|
|
|
get "/faq"
|
|
|
|
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.body).to include("Polish FAQ")
|
|
|
|
end
|
|
|
|
end
|
2021-12-21 14:51:18 -05:00
|
|
|
|
|
|
|
it "does not pollute SiteSetting.title (regression)" do
|
|
|
|
SiteSetting.title = "test"
|
|
|
|
SiteSetting.short_site_description = "something"
|
|
|
|
|
|
|
|
expect do
|
|
|
|
get "/login"
|
|
|
|
get "/login"
|
|
|
|
end.to_not change { SiteSetting.title }
|
|
|
|
end
|
2014-07-26 17:16:08 -04:00
|
|
|
end
|
2013-10-30 16:37:22 -04:00
|
|
|
|
2013-06-04 18:34:54 -04:00
|
|
|
describe "#enter" do
|
|
|
|
context "without a redirect path" do
|
|
|
|
it "redirects to the root url" do
|
2017-08-31 00:06:56 -04:00
|
|
|
post "/login.json"
|
|
|
|
expect(response).to redirect_to("/")
|
2013-06-04 18:34:54 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "with a redirect path" do
|
|
|
|
it "redirects to the redirect path" do
|
2017-08-31 00:06:56 -04:00
|
|
|
post "/login.json", params: { redirect: "/foo" }
|
|
|
|
expect(response).to redirect_to("/foo")
|
2013-06-04 18:34:54 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-08-28 17:45:13 -04:00
|
|
|
context "with a full url" do
|
|
|
|
it "redirects to the correct path" do
|
2017-08-31 00:06:56 -04:00
|
|
|
post "/login.json", params: { redirect: "#{Discourse.base_url}/foo" }
|
|
|
|
expect(response).to redirect_to("/foo")
|
2014-08-28 17:45:13 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-07-04 01:41:43 -04:00
|
|
|
context "with a redirect path with query params" do
|
|
|
|
it "redirects to the redirect path and preserves query params" do
|
|
|
|
post "/login.json", params: { redirect: "/foo?bar=1" }
|
|
|
|
expect(response).to redirect_to("/foo?bar=1")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-10-30 11:31:44 -04:00
|
|
|
context "with a period to force a new host" do
|
|
|
|
it "redirects to the root path" do
|
2017-08-31 00:06:56 -04:00
|
|
|
post "/login.json", params: { redirect: ".org/foo" }
|
|
|
|
expect(response).to redirect_to("/")
|
2014-10-30 11:31:44 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-15 02:39:37 -04:00
|
|
|
context "with a full url to an external host" do
|
2014-08-28 17:45:13 -04:00
|
|
|
it "redirects to the root path" do
|
2017-08-31 00:06:56 -04:00
|
|
|
post "/login.json", params: { redirect: "http://eviltrout.com/foo" }
|
|
|
|
expect(response).to redirect_to("/")
|
2014-08-28 17:45:13 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context "with an invalid URL" do
|
|
|
|
it "redirects to the root" do
|
2017-08-31 00:06:56 -04:00
|
|
|
post "/login.json", params: { redirect: "javascript:alert('trout')" }
|
|
|
|
expect(response).to redirect_to("/")
|
2014-08-28 17:45:13 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-06-11 07:40:16 -04:00
|
|
|
context "with an array" do
|
|
|
|
it "redirects to the root" do
|
|
|
|
post "/login.json", params: { redirect: ["/foo"] }
|
2019-06-17 07:14:30 -04:00
|
|
|
expect(response.status).to eq(400)
|
2020-05-07 11:04:12 -04:00
|
|
|
json = response.parsed_body
|
2019-06-17 07:14:30 -04:00
|
|
|
expect(json["errors"]).to be_present
|
|
|
|
expect(json["errors"]).to include(I18n.t("invalid_params", message: "redirect"))
|
2019-06-11 07:40:16 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-04 18:34:54 -04:00
|
|
|
context "when the redirect path is the login page" do
|
|
|
|
it "redirects to the root url" do
|
2017-08-31 00:06:56 -04:00
|
|
|
post "/login.json", params: { redirect: login_path }
|
|
|
|
expect(response).to redirect_to("/")
|
2013-06-04 18:34:54 -04:00
|
|
|
end
|
|
|
|
end
|
2024-07-15 02:39:37 -04:00
|
|
|
|
2024-11-27 11:22:45 -05:00
|
|
|
context "when the redirect path contains the '/login' string" do
|
|
|
|
it "redirects to the requested path" do
|
|
|
|
post "/login.json", params: { redirect: "/page/login/1" }
|
|
|
|
expect(response).to redirect_to("/page/login/1")
|
|
|
|
end
|
|
|
|
end
|
2024-07-15 02:39:37 -04:00
|
|
|
context "when the redirect path is invalid" do
|
|
|
|
it "redirects to the root URL" do
|
|
|
|
post "/login.json", params: { redirect: "test" }
|
|
|
|
expect(response).to redirect_to("/")
|
|
|
|
end
|
|
|
|
end
|
2013-06-04 18:34:54 -04:00
|
|
|
end
|
2022-02-14 07:47:56 -05:00
|
|
|
|
|
|
|
describe "#service_worker_asset" do
|
|
|
|
it "works" do
|
|
|
|
get "/service-worker.js"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.content_type).to start_with("application/javascript")
|
2024-02-16 10:03:05 -05:00
|
|
|
expect(response.body).to include("addEventListener")
|
2022-02-14 07:47:56 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it "replaces sourcemap URL" do
|
|
|
|
Rails
|
|
|
|
.application
|
|
|
|
.assets_manifest
|
|
|
|
.stubs(:find_sources)
|
|
|
|
.with("service-worker.js")
|
|
|
|
.returns([<<~JS])
|
|
|
|
someFakeServiceWorkerSource();
|
|
|
|
//# sourceMappingURL=service-worker-abcde.js.map
|
|
|
|
JS
|
|
|
|
|
2022-05-11 08:42:34 -04:00
|
|
|
{
|
|
|
|
"/assets/service-worker.js" => "/assets/service-worker-abcde.js.map",
|
|
|
|
"/assets/service-worker.js.br" => "/assets/service-worker-abcde.js.map",
|
|
|
|
"/assets/service-worker.br.js" => "/assets/service-worker-abcde.js.map",
|
|
|
|
"/assets/service-worker.js.gz" => "/assets/service-worker-abcde.js.map",
|
|
|
|
"/assets/service-worker.gz.js" => "/assets/service-worker-abcde.js.map",
|
|
|
|
"https://example.com/assets/service-worker.js" =>
|
|
|
|
"https://example.com/assets/service-worker-abcde.js.map",
|
|
|
|
"https://example.com/subfolder/assets/service-worker.js" =>
|
|
|
|
"https://example.com/subfolder/assets/service-worker-abcde.js.map",
|
|
|
|
}.each do |asset_path, expected_map_url|
|
|
|
|
ActionController::Base
|
|
|
|
.helpers
|
|
|
|
.stubs(:asset_path)
|
|
|
|
.with("service-worker.js")
|
|
|
|
.returns(asset_path)
|
|
|
|
|
|
|
|
get "/service-worker.js"
|
|
|
|
expect(response.status).to eq(200)
|
|
|
|
expect(response.content_type).to start_with("application/javascript")
|
|
|
|
expect(response.body).to include("sourceMappingURL=#{expected_map_url}\n")
|
|
|
|
end
|
2022-02-14 07:47:56 -05:00
|
|
|
end
|
|
|
|
end
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|