#![allow(clippy::expect_used, clippy::panic)]
const SOURCE: &str = include_str!("../assets/vanilla.js");
fn code_only(source: &str) -> String {
let mut out = String::with_capacity(source.len());
let mut rest = source;
while let Some(start) = rest.find("/*") {
out.push_str(&rest[..start]);
rest = rest[start..]
.find("*/")
.map_or("", |end| &rest[start + end + 2..]);
}
out.push_str(rest);
out.lines()
.map(|line| line.split_once("//").map_or(line, |(code, _)| code))
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn uses_no_dynamic_code_or_html_sinks() {
let code = code_only(SOURCE);
for sink in [
"eval(",
"new Function",
"Function(",
"innerHTML",
"outerHTML",
"insertAdjacentHTML",
"document.write",
"setTimeout('",
"setTimeout(\"",
"setInterval",
"javascript:",
] {
assert!(!code.contains(sink), "vanilla.js must not use `{sink}`");
}
}
#[test]
fn is_strict_and_wrapped() {
let code = code_only(SOURCE);
assert!(code.contains("'use strict'"), "strict mode");
assert!(
code.trim_start().starts_with("(function ()"),
"one IIFE, no globals other than window.Vanilla"
);
}
#[test]
fn hooks_into_htmx() {
let code = code_only(SOURCE);
assert!(code.contains("'htmx:load'"), "binds swapped content");
assert!(
code.contains("'htmx:beforeCleanupElement'"),
"cleans up removed content"
);
}
#[test]
fn registers_every_rust_behavior() {
let code = code_only(SOURCE);
for behavior in autumn_plugin_vanilla::Behavior::ALL {
let call = format!("register('{}'", behavior.name());
assert!(code.contains(&call), "vanilla.js must have `{call}`");
}
}
#[test]
fn registers_no_behavior_that_rust_lacks() {
let code = code_only(SOURCE);
let calls = code.matches("register('").count();
assert_eq!(
calls,
autumn_plugin_vanilla::Behavior::ALL.len(),
"each register call has a Behavior variant"
);
}
#[test]
fn guards_against_dom_clobbering() {
let code = code_only(SOURCE);
assert!(code.contains("Symbol.for('autumn-plugin-vanilla')"));
assert!(code.contains("Document.prototype.querySelector"));
assert!(!code.contains("if (window.Vanilla)"));
}
#[test]
fn comment_stripper_works() {
assert_eq!(code_only("a /* b */ c // d\ne"), "a c \ne");
assert_eq!(code_only("a /* open"), "a ");
}