discourse/spec/requests/theme_javascripts_controlle...

211 lines
7.7 KiB
Ruby

# frozen_string_literal: true
RSpec.describe ThemeJavascriptsController do
include ActiveSupport::Testing::TimeHelpers
before { ThemeJavascriptCompiler.disable_terser! }
after { ThemeJavascriptCompiler.enable_terser! }
def clear_disk_cache
if Dir.exist?(ThemeJavascriptsController::DISK_CACHE_PATH)
`rm -rf #{ThemeJavascriptsController::DISK_CACHE_PATH}`
end
end
let!(:theme) { Fabricate(:theme) }
let(:theme_field) { ThemeField.create!(theme: theme, target_id: 0, name: "header", value: "<a>html</a>") }
let(:javascript_cache) { JavascriptCache.create!(content: 'console.log("hello");', theme_field: theme_field) }
before { clear_disk_cache }
after { clear_disk_cache }
describe '#show' do
def update_digest_and_get(digest)
# actually set digest to make sure 404 is raised by router
javascript_cache.update(digest: digest)
get "/theme-javascripts/#{digest}.js"
end
it 'only accepts 40-char hexadecimal digest name' do
update_digest_and_get('0123456789abcdefabcd0123456789abcdefabcd')
expect(response.status).to eq(200)
update_digest_and_get('0123456789abcdefabcd0123456789abcdefabc')
expect(response.status).to eq(404)
update_digest_and_get('gggggggggggggggggggggggggggggggggggggggg')
expect(response.status).to eq(404)
update_digest_and_get('0123456789abcdefabc_0123456789abcdefabcd')
expect(response.status).to eq(404)
update_digest_and_get('0123456789abcdefabc-0123456789abcdefabcd')
expect(response.status).to eq(404)
update_digest_and_get('../../Gemfile')
expect(response.status).to eq(404)
end
it 'considers the database record as the source of truth' do
clear_disk_cache
get "/theme-javascripts/#{javascript_cache.digest}.js"
expect(response.status).to eq(200)
expect(response.body).to eq(javascript_cache.content)
expect(response.headers['Content-Length']).to eq(javascript_cache.content.bytesize.to_s)
javascript_cache.destroy!
get "/theme-javascripts/#{javascript_cache.digest}.js"
expect(response.status).to eq(404)
end
it "adds sourceMappingUrl if there is a source map" do
digest = SecureRandom.hex(20)
javascript_cache.update(digest: digest)
get "/theme-javascripts/#{digest}.js"
expect(response.status).to eq(200)
expect(response.body).to eq('console.log("hello");')
digest = SecureRandom.hex(20)
javascript_cache.update(digest: digest, source_map: '{fakeSourceMap: true}')
get "/theme-javascripts/#{digest}.js"
expect(response.status).to eq(200)
expect(response.body).to eq <<~JS
console.log("hello");
//# sourceMappingURL=#{digest}.map?__ws=test.localhost
JS
end
end
describe "#show_map" do
it "returns a source map when present" do
get "/theme-javascripts/#{javascript_cache.digest}.map"
expect(response.status).to eq(404)
digest = SecureRandom.hex(20)
javascript_cache.update(digest: digest, source_map: '{fakeSourceMap: true}')
get "/theme-javascripts/#{digest}.map"
expect(response.status).to eq(200)
expect(response.body).to eq("{fakeSourceMap: true}")
javascript_cache.destroy
get "/theme-javascripts/#{digest}.map"
expect(response.status).to eq(404)
end
end
describe "#show_tests" do
let(:component) { Fabricate(:theme, component: true, name: 'enabled-component') }
let!(:tests_field) do
field = component.set_field(
target: :tests_js,
type: :js,
name: "acceptance/some-test.js",
value: "assert.ok(true);"
)
component.save!
field
end
before do
ThemeField.create!(
theme: component,
target_id: Theme.targets[:settings],
name: "yaml",
value: "num_setting: 5"
)
component.save!
end
it "forces theme settings default values" do
component.update_setting(:num_setting, 643)
_, digest = component.baked_js_tests_with_digest
get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
expect(response.body).to include("require(\"discourse/lib/theme-settings-store\").registerSettings(#{component.id}, {\"num_setting\":5}, { force: true });")
expect(response.body).to include("assert.ok(true);")
end
it "includes theme uploads URLs in the settings object" do
SiteSetting.authorized_extensions = "*"
js_file = Tempfile.new(["vendorlib", ".js"])
js_file.write("console.log(123);\n")
js_file.rewind
js_upload = UploadCreator.new(js_file, "vendorlib.js").create_for(Discourse::SYSTEM_USER_ID)
component.set_field(
type: :theme_upload_var,
target: :common,
name: "vendorlib",
upload_id: js_upload.id
)
component.save!
_, digest = component.baked_js_tests_with_digest
get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
expect(response.body).to include(
"require(\"discourse/lib/theme-settings-store\").registerSettings(" +
"#{component.id}, {\"num_setting\":5,\"theme_uploads\":{\"vendorlib\":" +
"\"/uploads/default/test_#{ENV['TEST_ENV_NUMBER'].presence || '0'}/original/1X/#{js_upload.sha1}.js\"},\"theme_uploads_local\":{\"vendorlib\":" +
"\"/theme-javascripts/#{js_upload.sha1}.js?__ws=test.localhost\"}}, { force: true });"
)
expect(response.body).to include("assert.ok(true);")
ensure
js_file&.close
js_file&.unlink
end
it "responds with 404 if digest is not a 40 chars hex" do
digest = Rack::Utils.escape('../../../../../../../../../../etc/passwd').gsub('.', '%2E')
get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
expect(response.status).to eq(404)
get "/theme-javascripts/tests/#{component.id}-abc123.js"
expect(response.status).to eq(404)
end
it "responds with 404 if theme does not exist" do
get "/theme-javascripts/tests/#{Theme.maximum(:id) + 1}-#{SecureRandom.hex(20)}.js"
expect(response.status).to eq(404)
end
it "responds with 304 if tests digest has not changed" do
content, digest = component.baked_js_tests_with_digest
get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
last_modified = Time.rfc2822(response.headers["Last-Modified"])
expect(response.status).to eq(200)
expect(response.headers["Content-Length"].to_i).to eq(content.size)
get "/theme-javascripts/tests/#{component.id}-#{digest}.js",
headers: { "If-Modified-Since" => (last_modified + 10.seconds).rfc2822 }
expect(response.status).to eq(304)
end
it "responds with 404 to requests with old digests" do
_, old_digest = component.baked_js_tests_with_digest
get "/theme-javascripts/tests/#{component.id}-#{old_digest}.js"
expect(response.status).to eq(200)
expect(response.body).to include("assert.ok(true);")
tests_field.update!(value: "assert.ok(343434);")
tests_field.invalidate_baked!
_, digest = component.baked_js_tests_with_digest
expect(old_digest).not_to eq(digest)
get "/theme-javascripts/tests/#{component.id}-#{old_digest}.js"
expect(response.status).to eq(404)
get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
expect(response.status).to eq(200)
expect(response.body).to include("assert.ok(343434);")
end
it "includes inline sourcemap" do
ThemeJavascriptCompiler.enable_terser!
content, digest = component.baked_js_tests_with_digest
get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
expect(response.status).to eq(200)
expect(response.body).to include("//# sourceMappingURL=data:application/json;base64,")
end
end
end