use super::html_escape;
use crate::ui::config::ScalarConfig;
pub(crate) fn render(spec_url: &str, title: &str, config: &ScalarConfig) -> String {
const DEFAULT_CDN: &str = "https://cdn.jsdelivr.net/npm/@scalar/api-reference";
let cdn = config.cdn_url.as_deref().unwrap_or(DEFAULT_CDN);
let cdn = html_escape(cdn);
let title = html_escape(title);
let spec_url = html_escape(spec_url);
let config_json = serde_json::to_string(config)
.expect("ScalarConfig serialization is infallible for a flat owned struct");
let config_json = html_escape(&config_json);
format!(
r##"<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
<style>
html, body {{ margin: 0; padding: 0; height: 100%; }}
</style>
</head>
<body>
<script
id="api-reference"
data-url="{spec_url}"
data-configuration="{config_json}"
></script>
<script src="{cdn}"></script>
</body>
</html>
"##
)
}
#[cfg(test)]
mod tests {
use super::*;
fn default_cfg() -> ScalarConfig {
ScalarConfig::default()
}
#[test]
fn renders_with_supplied_spec_url() {
let html = render("/openapi.json", "Test API", &default_cfg());
assert!(html.contains(r#"data-url="/openapi.json""#));
assert!(html.contains("<title>Test API</title>"));
assert!(html.contains("@scalar/api-reference"));
assert!(html.contains(r#"id="api-reference""#));
}
#[test]
fn escapes_dangerous_characters_in_title() {
let html = render("/openapi.json", "<script>alert(1)</script>", &default_cfg());
assert!(html.contains("<script>"));
assert!(!html.contains("<title><script>alert(1)</script></title>"));
}
#[test]
fn escapes_quotes_in_spec_url() {
let html = render(r#"/openapi.json" onload="alert(1)"#, "x", &default_cfg());
assert!(html.contains("""));
assert!(!html.contains(r#"data-url="/openapi.json" onload="alert(1)"#));
}
#[test]
fn renders_with_default_cdn_when_cdn_url_is_none() {
let html = render("/openapi.json", "t", &default_cfg());
assert!(html.contains("cdn.jsdelivr.net/npm/@scalar/api-reference"));
}
#[test]
fn renders_with_overridden_cdn_url_when_set() {
let cfg = default_cfg().cdn_url("https://internal.example.com/scalar.js");
let html = render("/openapi.json", "t", &cfg);
assert!(html.contains("https://internal.example.com/scalar.js"));
assert!(!html.contains("cdn.jsdelivr.net"));
}
#[test]
fn cdn_url_is_not_serialized_into_scalar_config() {
let cfg = default_cfg().cdn_url("https://internal.example.com/scalar.js");
let json = serde_json::to_string(&cfg).unwrap();
assert!(!json.contains("cdnUrl"));
assert!(!json.contains("cdn_url"));
assert!(!json.contains("internal.example.com"));
}
}