use autumn_web::assets::PluginAssets;
pub const ASSETS_NAMESPACE: &str = "motion";
pub(crate) const MOTION_JS: &str = "motion.min.js";
pub(crate) const INIT_JS: &str = "init.js";
pub(crate) const MOTION_CSS: &str = "motion.css";
pub static MOTION_ASSETS: PluginAssets = PluginAssets::from_files(
ASSETS_NAMESPACE,
&[
(MOTION_JS, include_bytes!("../assets/motion.min.js")),
(INIT_JS, include_bytes!("../assets/init.js")),
(MOTION_CSS, include_bytes!("../assets/motion.css")),
],
);
pub const MOTION_VERSION: &str = "12.43.0";
pub const MOTION_SOURCE: &str = "https://cdn.jsdelivr.net/npm/motion@12.43.0/dist/motion.js";
pub const MOTION_JS_INTEGRITY: &str =
"sha384-hgVFh5YKDMdpcWEjpRqk2kPXOKBH7I9NBCKe3uR6dHKgy/BWPe8kyL01kiYuRn1u";
#[cfg(test)]
mod tests {
use super::*;
use base64::Engine as _;
use sha2::{Digest as _, Sha384};
fn sri(bytes: &[u8]) -> String {
let digest = Sha384::digest(bytes);
format!(
"sha384-{}",
base64::engine::general_purpose::STANDARD.encode(digest)
)
}
#[test]
fn bundle_holds_exactly_the_three_served_files() {
let files: Vec<&str> = MOTION_ASSETS
.iter()
.map(autumn_web::assets::PluginAsset::logical_path)
.collect();
assert_eq!(files, [INIT_JS, MOTION_CSS, MOTION_JS]);
assert_eq!(MOTION_ASSETS.namespace(), ASSETS_NAMESPACE);
assert_eq!(MOTION_ASSETS.mount_path(), "/static/_plugins/motion");
}
#[test]
fn bundle_integrity_matches_embedded_bytes() {
for asset in MOTION_ASSETS.iter() {
assert_eq!(
asset.integrity(),
sri(asset.bytes()),
"{} SRI is computed from its bytes",
asset.logical_path()
);
}
}
#[test]
fn vendored_motion_matches_the_pinned_upstream_hash() {
let motion = MOTION_ASSETS
.get(MOTION_JS)
.expect("motion.min.js is bundled");
assert_eq!(sri(motion.bytes()), MOTION_JS_INTEGRITY);
assert_eq!(motion.integrity(), MOTION_JS_INTEGRITY);
}
#[test]
fn urls_are_fingerprinted_under_the_plugin_mount() {
for asset in MOTION_ASSETS.iter() {
let path = asset.logical_path();
assert_eq!(asset.plain_url(), format!("/static/_plugins/motion/{path}"));
let (stem, ext) = path
.rsplit_once('.')
.expect("bundled files have extensions");
let url = asset.url();
let hash = url
.strip_prefix(&format!("/static/_plugins/motion/{stem}."))
.and_then(|rest| rest.strip_suffix(&format!(".{ext}")))
.unwrap_or_else(|| panic!("{url} is the fingerprinted form of {path}"));
assert_eq!(hash.len(), 8, "{url}");
assert!(hash.bytes().all(|b| b.is_ascii_hexdigit()), "{url}");
}
}
#[test]
fn content_types_match_the_files() {
let content_type = |path| {
MOTION_ASSETS
.get(path)
.expect("file is bundled")
.content_type()
};
assert_eq!(content_type(MOTION_JS), "text/javascript; charset=utf-8");
assert_eq!(content_type(INIT_JS), "text/javascript; charset=utf-8");
assert_eq!(content_type(MOTION_CSS), "text/css; charset=utf-8");
}
#[test]
fn manifest_agrees_with_constants() {
let manifest = include_str!("../assets/manifest.json");
assert!(
manifest.contains(MOTION_VERSION),
"manifest records the pinned version"
);
assert!(
manifest.contains(MOTION_SOURCE),
"manifest records the source URL"
);
assert!(
manifest.contains(MOTION_JS_INTEGRITY),
"manifest records the motion.min.js integrity"
);
}
}